Rainer Stropek | @rstropek

Traits
Not Your Grandparents' Interfaces

Introduction

Rainer Stropek

  • Passionate software developers for 25+ years
     
  • Microsoft MVP, Regional Director
     
  • Trainer, Teacher, Mentor
     
  • 💕 community

Tic Tac Toe

Let's build a (slightly over-engineered 😜) TicTacToe game and learn about traits
on the way.

Sample code is available on GitHub

The Basics

Basics

pub trait Computer {
    fn get_answer(&self) -> u8;
}

pub struct DeepThought;

impl Computer for DeepThought {
    fn get_answer(&self) -> u8 {
        42
    }
}

fn main() {
    let c = DeepThought {};
    let _result = c.get_answer();
}
<playground::DeepThought as playground::Computer>::get_answer:
	pushq	%rax
	movq	%rdi, (%rsp)
	movb	$42, %al
	popq	%rcx
	retq

playground::main:
	pushq	%rax
	movq	%rsp, %rdi
	callq	<playground::DeepThought as playground::Computer>::get_answer
	movb	%al, 7(%rsp)
	popq	%rax
	retq

Basics

  • Trait ToCompactString, implemented by BoardContent
  • SquareContent
    • ​Enum implementing traits
    • System traits Default, From/Into
  • ​BoardIndex
    • System trait FromStr --> parse
    • System trait Display --> println!
    • System traits Add, Sub --> +/-
    • How do we get Into "for free"?

Let's get more advanced

  • BoardContent
    • Trait ToCompactString, implemented on top of another trait
  • Iterators
    • Make BoardContent iterable --> IntoIterator
  • ​Error Types
    • Implement Display trait for error type
  • Indexer
    • System traits Index, IndexMut --> []

Let's get more advanced

  • Game
    • Build a game controller without dependency to BoardContent
    • Build it on top of a trait (SquareAccessor --> Index --> Game)

What happens
behind the
scenes?

use std::io::*;

pub trait Animal {
    fn make_sound(&self) -> &'static str;
}

pub struct Cat;
pub struct Dog;

impl Animal for Cat {
    fn make_sound(&self) -> &'static str {
        "Miau\n"
    }
}

impl Animal for Dog {
    fn make_sound(&self) -> &'static str {
        "Wuff\n"
    }
}

fn print_sound(animal: &impl Animal) {
    stdout().write(animal.make_sound().as_bytes()).unwrap();
}

fn main() {
    // Static dispatching

    let a = Cat {};
    print_sound(&a);

    let a = Dog {};
    print_sound(&a);
}
<playground::Cat as playground::Animal>::make_sound:
	...
	leaq	.L__unnamed_4(%rip), %rax   ; Miau
	...
	retq

<playground::Dog as playground::Animal>::make_sound:
	...
	leaq	.L__unnamed_5(%rip), %rax   ; Wuff
	...
	retq

playground::print_sound:
	...
	callq	*std::io::stdio::stdout@GOTPCREL(%rip)
	...
	callq	<playground::Dog as playground::Animal>::make_sound
    ...
	callq	*<std::io::stdio::Stdout as std::io::Write>::write@GOTPCREL(%rip)
	...
	retq

playground::print_sound:
	...
	callq	*std::io::stdio::stdout@GOTPCREL(%rip)
	...
	callq	<playground::Cat as playground::Animal>::make_sound
	...
	callq	*<std::io::stdio::Stdout as std::io::Write>::write@GOTPCREL(%rip)
	...
	retq

playground::main:
	subq	$24, %rsp
	leaq	8(%rsp), %rdi
	callq	playground::print_sound    ; Cat
	leaq	16(%rsp), %rdi
	callq	playground::print_sound    ; Dog
	addq	$24, %rsp
	retq

Direct function calls

Dynamic Dispatch

use std::io::*;

pub trait Animal {
    fn make_sound(&self) -> &'static str;
}

pub struct Cat;
pub struct Dog;

impl Animal for Cat {
    fn make_sound(&self) -> &'static str {
        "Miau\n"
    }
}

impl Animal for Dog {
    fn make_sound(&self) -> &'static str {
        "Wuff\n"
    }
}

fn print_sound(animal: &dyn Animal) {
    stdout().write(animal.make_sound().as_bytes()).unwrap();
}

fn main() {
    let da: &dyn Animal = &Cat {};
    print_sound(da);

    let da: &dyn Animal = &Dog {};
    print_sound(da);
}
playground::print_sound:
	...
	movq	%rax, 80(%rsp)
	movq	48(%rsp), %rax
	movq	40(%rsp), %rdi
	callq	*24(%rax)
	...
	retq

playground::main:
	...
	leaq	.L__unnamed_3(%rip), %rax
	movq	%rax, 16(%rsp)
	leaq	.L__unnamed_9(%rip), %rdi
	leaq	.L__unnamed_3(%rip), %rsi
	callq	playground::print_sound
	...
	leaq	.L__unnamed_4(%rip), %rax
	movq	%rax, 32(%rsp)
	leaq	.L__unnamed_9(%rip), %rdi
	leaq	.L__unnamed_4(%rip), %rsi
	callq	playground::print_sound
	addq	$40, %rsp
	retq

...

.L__unnamed_3:
	.quad	core::ptr::drop_in_place<playground::Cat>
	.quad	0
	.quad	1
	.quad	<playground::Cat as playground::Animal>::make_sound

.L__unnamed_4:
	.quad	core::ptr::drop_in_place<playground::Dog>
	.quad	0
	.quad	1
	.quad	<playground::Dog as playground::Animal>::make_sound

Indirect function call

Traits 🤘

Rainer Stropek | @rstropek

Traits - Not Your Grandparents' Interfaces

By Rainer Stropek

Traits - Not Your Grandparents' Interfaces

  • 812