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

  • 261