Skip to content
Snippets Groups Projects
Commit 38748e2b authored by Per Lindgren's avatar Per Lindgren
Browse files

examples

parent cfd6b987
No related branches found
No related tags found
No related merge requests found
// a struct with generics
// a trait for double
// a function with trait bound
use std::fmt::Debug;
#[derive(Debug)]
struct P<T> {
x: T,
y:
pub trait Iterator<T> {T,
y: T,
}
trait Double {
......@@ -21,13 +21,15 @@ impl Double for P<u32> {
}
}
impl Double for P<u64> {
impl Double for String {
fn double(&mut self) {
self.x += self.x;
self.y += self.y;
let s = self.clone();
self.push_str(&s);
}
}
// This is a generic function over types
// implementing Double.
fn quad<T>(x: &mut T)
where
T: Double,
......@@ -41,19 +43,18 @@ fn main() {
quad(&mut p1);
println!("{:?}", p1);
let mut p2 = P { x: 1u64, y: 2 };
quad(&mut p2);
println!("{:?}", p2);
let mut s = "String, ".to_string();
quad(&mut s);
println!("{:?}", s);
}
// The types of p1 and p2 is known at compile time
// (See that rust analyses derives the types)
// Types of p1 and s are known at compile time
//
// The compiler will "specialize" the call
// to double (as cheap as an ordinary call)
// The compiler will "specialize" the calls
// to double (as cheap as an ordinary calls)
//
// The quad function takes a generic parameter T
// the T : Double, is "trait bound"
// the T : Double, is a "trait bound"
// meaning T is generic but must implement T
//
// As the the type at the call site is known
......@@ -63,7 +64,4 @@ fn main() {
// The implementation is as efficient as an
// as if written as ordinary functions.
//
// Specialization is also called "monomorphization".
// Also C++ does this (but through templates).
//
// Zero-cost abstractions is the key to efficiency.
// We have zero-cost abstractions.
......@@ -9,6 +9,11 @@ struct P<T> {
y: T,
}
// Debug is a super trait of Double
// I.e, all types implementing Double must also implement
// Debug, and as a consequence:
// All types implementing Double is ensured to implement
// Debug.
trait Double: Debug {
fn double(&mut self);
}
......@@ -20,13 +25,6 @@ impl Double for P<u32> {
}
}
impl Double for P<u64> {
fn double(&mut self) {
self.x += self.x;
self.y += self.y;
}
}
impl Double for String {
fn double(&mut self) {
let s = self.clone();
......@@ -34,21 +32,11 @@ impl Double for String {
}
}
fn quad<T>(x: &mut T)
where
T: Double,
{
x.double();
x.double();
}
fn main() {
let mut p1 = P { x: 1u32, y: 2 };
quad(&mut p1);
println!("{:?}", p1);
let mut s = "String".to_string();
quad(&mut s);
let mut s = "String, ".to_string();
println!("{:?}", s);
let mut v: Vec<&mut dyn Double> = Vec::new();
......@@ -59,7 +47,7 @@ fn main() {
e.double();
// Here `e` implements Double
// However we don't know at compile time
// which implementation (u32, u64 or String)
// which implementation (u32 or String)
// so the compiler will generate
// a dynamic dispatch.
//
......@@ -78,7 +66,7 @@ fn main() {
// Since debug is a Super trait of Double
// The compiler knows that Debug is implemented
// for each type that implement Double
// (P<u32>, P<u64> and String)
// (P<u32> and String)
//
// However it does not know at compile time
// the concrete type of the vector elements,
......@@ -88,6 +76,7 @@ fn main() {
// Notice, the overhead of dynamic dispatch is
// merely a pointer indirection, so not a big deal right?
//
// Hmmm, not really....
// The performance kill is likely NOT the additional pointer read
// but rather that other optimizations are not possible.
//
......
......@@ -39,8 +39,9 @@ fn main() {
println!("{:?}", s);
}
// Types of p1 and s is known at compile time
// Even if we now use Traits the compiler can
// specialize the calls to double.
// Types of p1 and s are known at compile time
//
// We have a zero-cost abstraction.
// The compiler will "specialize" the calls
// to double (as cheap as an ordinary calls)
//
// We have zero-cost abstractions.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment