[The Rust Programming Language] 19. Advanced Features

19. Advanced Features

19.3 Advanced Types

Using the Newtype Pattern for Type Safety and Abstraction

Creating Type Synonyms with Type Aliases

Rust provides the ability to declare a type alias to give an existing type another name. For this we use the type keyword.

The main use case for type Alias(synonyms) is to reduce repetition.

1
2
3
4
5
6
7
8
9
let f: Box<dyn Fn() + Send + 'static> = Box::new(|| println!("hi"));

fn takes_long_type(f: Box<dyn Fn() + Send + 'static>) {
// --snip--
}

fn returns_long_type() -> Box<dyn Fn() + Send + 'static> {
// --snip--
}

A type alias makes this code more manageable by reducing the repetition.

1
2
3
4
5
6
7
8
9
10
11
type Thunk = Box<dyn Fn() + Send + 'static>;

let f: Thunk = Box::new(|| println!("hi"));

fn takes_long_type(f: Thunk) {
// --snip--
}

fn returns_long_type() -> Thunk {
// --snip--
}

Type aliases are also commonly used with the Result<T, E> type for reducing repetition.

The Result<..., Error> is repeated a lot. As such, std::io has this type alias declaration:

1
type Result<T> = std::result::Result<T, std::io::Error>;

Because this declaration is in the std::io module, we can use the fully qualified alias std::io::Result<T>—that is, a Result<T, E>with theEfilled in asstd::io::Error`. The Write trait function signatures end up looking like this:

1
2
3
4
5
6
7
pub trait Write {
fn write(&mut self, buf: &[u8]) -> Result<usize>;
fn flush(&mut self) -> Result<()>;

fn write_all(&mut self, buf: &[u8]) -> Result<()>;
fn write_fmt(&mut self, fmt: fmt::Arguments) -> Result<()>;
}

! The Never Type that Never Returns

Rust has a special type named ! that’s known in type theory lingo as the empty type because it has no values. We prefer to call it the never type because it stands in the place of the return type when a function will never return. Here is an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
fn bar() -> ! {
// --snip--
}

let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue, // Because ! can never have a value, Rust decides that the type of guess is u32.
};

impl<T> Option<T> {
pub fn unwrap(self) -> T {
match self {
Some(val) => val,
None => panic!("called `Option::unwrap()` on a `None` value"),
}
}
}

print!("forever ");

loop {
print!("and ever ");
// break;
} // ! is the value of the expression.

// However, this wouldn’t be true if we included a break, because the loop would terminate when it got to the break.

Dynamically Sized Types and the Sized Trait

Rust needs to know how much memory to allocate for any value of a particular type, and all values of a type must use the same amount of memory.

1
2
3
4
5
// let s1: str = "Hello there!";   // expected `str`, found `&str`
// let s2: str = "How's it going?"; // expected `str`, found `&str`

let s1: &str = "Hello there!";
let s2: &str = "How's it going?";

If Rust allowed us to write this code, these two str values would need to take up the same amount of space. But they have different lengths: s1 needs 12 bytes of storage and s2 needs 15. This is why it’s not possible to create a variable holding a dynamically sized type.

So although a &T is a single value that stores the memory address of where the T is located, a &str is two values: the address of the str and its length. As such, we can know the size of a &str value at compile time: it’s twice the length of a usize. That is, we always know the size of a &str, no matter how long the string it refers to is. In general, this is the way in which dynamically sized types are used in Rust: they have an extra bit of metadata that stores the size of the dynamic information. The golden rule of dynamically sized types is that we must always put values of dynamically sized types behind a pointer of some kind.

References

[1] The Rust Programming Language - The Rust Programming Language - https://doc.rust-lang.org/book/title-page.html

[2] Advanced Features - The Rust Programming Language - https://doc.rust-lang.org/book/ch19-00-advanced-features.html