🦀 Rust - Traits
Use structs over creating new traits. Traits are for library code you expose. Like interfaces in many languages; if you have three or more structs that all need to implement an interface, you might consider introducing custom traits. You wouldn't want your Rust to feel like Java, would you?
Implementing traits is essential, introducing new traits is not.
Traits are used to signify something that multiple types have in common.
trait Signed {
fn is_positive(self) -> bool;
}
struct Vec2 {
x: f64,
y: f64,
}
impl Signed for Vec2 {
fn is_positive(self) -> bool {
self.x > 0.0 && self.y > 0.0
}
}
impl Signed for i32 {
fn is_positive(self) -> bool {
self > 0
}
}
fn main() {
let v1 = Vec2 { x: 1.0, y: 3.0 };
let v2 = Vec2 { x: -2.0, y: 1.5 };
let i1: i32 = 10;
assert_eq!(v1.is_positive(), true);
assert_eq!(v2.is_positive(), false);
assert_eq!(i1.is_positive(), true);
}
You can implement foreign traits. impl block is always for a specific type, so Self means the type itself.
struct Vec2 {
x: f64,
y: f64,
}
impl std::ops::Neg for Vec2 {
type Output = Self;
fn neg(self) -> Self {
Self {
x: -self.x,
y: -self.y,
}
}
}
fn main() {
let v1 = Vec2 { x: 1.0, y: 3.0 };
let v2 = -v1;
assert_eq!(v2.x, -1.0);
assert_eq!(v2.y, -3.0);
}
Trait methods can take self by reference or mutable reference.
impl std::clone::Clone for Vec2 {
fn clone(&self) -> Self {
Self { ..*self }
}
}
Marker traits don't define implementation but say that something is allowed.
// "copying the value is allowed" (also requires `Clone` to be implemented)
impl std::marker::Copy for Vec2 {}
derive attribute can be used to automatically implement traits. If a derive macro has been implemented for that trait, that is.
#[derive(Clone, Copy)]
struct Vec2 {
x: f64,
y: f64,
}
Some common traits in the standard library:
Copy: can be duplicated with an always efficient bitwise copyClone: can be duplicated with a potentially expensive deep copyDefault: the type has a default valueHash: can be mapped to a value of sized size by a hash functionDeref: make the struct behave like a reference to something else that it actually isDrop: what to do when the instance goes out-of-scope; e.g., close file or connection
Ordering
Ord: can always be orderedPartialOrd: can be ordered, but there might be corner cases
When derived on structs, it will produce a lexicographic ordering based on the top-to-bottom declaration order of the struct’s members.
Equality
Eq: can be compared andx==xis always truePartialEq: can be compared butx==xmight not always be true
For example, floating points are only
PartialEqbecauseNaN != NaN.