Prev: introduction Next: concepts
let x: i32 = 42;
let y: i16 = x;
You can’t put a larger integer into a smaller one:
: mismatched types
error[E0308]--> use-types/src/main.rs:14:22
|
14 | let y: i16 = x;
| --- ^ expected `i16`, found `i32`
| |
| expected due to this
|
: you can convert an `i32` to an `i16` and panic if the converted value doesn't fit
help|
14 | let y: i16 = x.try_into().unwrap();
| ++++++++++++++++++++
Rust also doesn’t allow things that appear “safe”, like putting an i32 in an i64:
let x = 42i32; // Integer literal with type suffix
let y: i64 = x;
Unlike in C, a char is a 4-byte type. You can convert chars to u32s just fine, but not the other way around (not all u32s are valid chars), so you have functions that require error handling:
char::from_u32
char::from_u32_unchecked
You can also use the newtype idiom to wrap a type:
type side = bool;
fn print(sides: side);
Enums can also encode data, which makes them extremely powerful.
To allow for values that can be null, use
Option<T>
, and for functions that can fail, use
Result<T, E>
.
Function pointers can be used in Rust:
fn sum(x: i32, y: i32) -> i32 {
+ y
x }
// Function pointer. Needs a type.
let op: fn(i32, i32) -> i32 = sum;
But they’re limiting: in exchange, Rust offers three closure traits:
When writing code that accepts closures, use the most general Fn trait that works.
As well, prefer Fn trait bounds to bare function pointers.
And prefer accepting trait types to concrete types if future flexibility is anticipated.
fn mean(v: &vec<u8>); // accepts vector
fn mean(v: &[u8]); // accepts slice and vector
fn mean<T: Iterator<Item = T>>(iter: T); // accept any iterable
If you want to encode a constraint into the type system but the type system can’t check it, use a marker trait.
A marker trait has no methods, but requires the implementor to implement it.
pub trait Sort {
/// Re-arrange contents into sorted order.
fn sort(&mut self);
}
/// Marker trait to indicate that a [`Sortable`] sort stably.
pub trait StableSort: Sort {}
As well, you’ll encounter generic code that has lots of requirements:
pub fn debug_sorted<T>(mut collection: T)
where
: Sort + IntoIterator,
TT::Item: Debug,
{
// Next line requires `T: Sort` trait bound.
.sort();
collection// Next line requires `T: IntoIterator` trait bound.
for item in collection {
// Next line requires `T::Item : Debug` trait bound
println!("{:?}", item);
}
}
You can also create a trait object, which requires
interacting through the concrete type through a pointer (either
&dyn Trait
or Box<dyn Trait>
).
Prev: introduction Next: concepts