Rust Traits -
not your grandparents interfaces
Rainer Stropek | time cockpit
Basics
pub struct ConsultingWork {
what: String,
hours: f32,
rate: f32,
}
pub trait Billable {
fn total(&self) -> f32;
}
impl Billable for ConsultingWork {
fn total(&self) -> f32 {
self.hours * self.rate
}
}
pub fn print_total(b: &impl Billable) {
println!("Total: ${:.2}", b.total());
}
fn main() {
let b = basics::ConsultingWork::new("AI Consulting", 10.0, 100.0);
basics::print_total(&b);
}
Traits on foreign types
pub trait Billable {
fn total(&self) -> f32;
}
impl Billable for f32 {
fn total(&self) -> f32 {
*self
}
}
pub fn print_total(b: &impl Billable) {
println!("Total: ${:.2}", b.total());
}
fn main() {
let b = 500.0;
foreign_types::print_total(&b);
}
pub struct MyF32(pub f32); // Newtype pattern
impl Deref for MyF32 {
type Target = f32;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Default for MyF32 {
fn default() -> Self {
MyF32(-1.0)
}
}
fn main() {
use foreign_types;
let b: foreign_types::MyF32 = Default::default();
foreign_types::print_total(b.deref());
}
impl Default for f32 {
fn default() -> Self {
-1.0
}
}
Blanket implementations
of Traits
pub trait Billable {
fn total(&self) -> f32;
}
pub trait Pointworthy {
fn points(&self) -> i32;
}
impl<T: Billable> Pointworthy for T {
fn points(&self) -> i32 {
(self.total() / 10.0) as i32
}
}
pub fn print_points(b: &impl Pointworthy) {
println!("Points: {}", b.points());
}
pub enum ShirtSize { Small, Medium, Large, }
pub struct CodingWork {
size: ShirtSize,
}
impl Billable for CodingWork {
fn total(&self) -> f32 {
match self.size {
ShirtSize::Small => 100.0,
ShirtSize::Medium => 200.0,
ShirtSize::Large => 1000.0,
}
}
}
fn main() {
let b = blanket_impl::CodingWork::new(...);
// b impls Billable, so impls Pointworthy
blanket_impl::print_points(&b);
}
Traits in the std library
#[derive(Debug, Clone, Default, PartialEq)]
pub struct ConsultingWork { ... }
impl Add for ConsultingWork {
type Output = ConsultingWork;
fn add(self, other: ConsultingWork) -> ConsultingWork {
if self != Default::default() {
ConsultingWork {
what: format!("{}, {}", self.what, other.what),
hours: self.hours + other.hours,
rate: self.rate + other.rate,
}
} else { other }
}
}
fn main() {
use std_traits;
let b1 = std_traits::ConsultingWork { what: "AI Consulting"... };
let b2 = std_traits::ConsultingWork { what: "Rust Coding"... };
let c = b1 + b2;
std_traits::print_work(&c);
}
impl Sum for ConsultingWork {
fn sum<I: Iterator<Item = ConsultingWork>>(iter: I) -> ConsultingWork {
iter.fold(ConsultingWork::default(), |a, b| a + b)
}
}
fn main() {
let b1 = std_traits::ConsultingWork { what: "AI Consulting"... };
let b2 = std_traits::ConsultingWork { what: "Rust Coding"... };
let b = [b1, b2];
std_traits::print_work(&b.into_iter().sum());
}
Trait objects
pub fn create_billable(what: &str, hours: f32, rate: f32) -> impl Billable {
ConsultingWork {
what: what.to_string(),
hours,
rate,
}
}
pub fn create_billable2(what: Option<&str>, hours: f32, rate: f32)
-> Box<dyn Billable> {
if let Some(what) = what {
Box::new(ConsultingWork {
what: what.to_string(),
hours,
rate: rate,
})
} else {
Box::new(hours * rate)
}
}
pub fn print_total(b: &dyn Billable) {
println!("Total: ${:.2}", b.total());
}
fn main() {
let b = trait_objects::create_billable("AI Consulting", 10.0, 100.0);
trait_objects::print_total(&b);
let b = trait_objects::create_billable2(None, 10.0, 100.0);
trait_objects::print_total(b.as_ref());
}
Q&A
Traits - Not your grandparents interfaces
By Rainer Stropek
Traits - Not your grandparents interfaces
- 228