Note: only took note on selected portion
Associated types connect a type placeholder with a trait such that the trait method definition can use these placeholder types in their signatures. The implementor of a trait will specify the concrete type to be used instead of the placeholder type for the particular implementation.
i.e.
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
// --snip--
Compared to generics, this approach does not require programmer to annotate the types in each implementation. When a trait has a generic parameter, it can be implemented for a type multiple times, changing the concrete types of the generic type parameters each time.
Newtype pattern is used to abstract away some implementation details of the type: the new type cna expose a public API that’s different from the API of the private inner type. It can also be used to wrap up existing type and do operations on them.
For example, implementing a trait outside of crate for a type outside of crate.
use std::fmt;
struct Wrapper(Vec<String>);
impl fmt::Display for Wrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}]", self.0.join(", "))
}
}
fn main() {
let w = Wrapper(vec![String::from("hello"), String::from("world")]);
println!("w = {}", w);
}
Internal implementations could also be hidden using newtype. A People
type could be used to wrap HashMap<i32, String>
that stores a person’s ID associated with their name. Code using People
would only interact with the public API the program provides.
This pattern is a lightweight way to achieve encapsulation to hide implementation details.
type Kilometers = i32;
This would simply create an alias, so one can pass Kilometers
values to function that takes i32
parameters, and vise versa.
The main use case for type synonyms is to reduce repetition. For instance, to use
type Thunk = Box<dyn Fn() + Send + 'static>;