Skip to content

Instantly share code, notes, and snippets.

@EddiG
Last active August 4, 2023 06:21
Show Gist options
  • Save EddiG/a8fa82a6754b165ba7aaa2585cf2b4d7 to your computer and use it in GitHub Desktop.
Save EddiG/a8fa82a6754b165ba7aaa2585cf2b4d7 to your computer and use it in GitHub Desktop.

Rust

Blanket implementation of a trait

Here the Speak trait has the blanket implementation for the references of any type that implement the Speak trait (Human type in the current example). That allows to pass Human, &Human, and &mut Human to the introduce method that expects a trait that has Speak trait implemented. The blanket implementation is necessary as Rust doesn't dereference types in traits.

trait Speak {
    fn speak(&self);
}

// Blanket implementation for &T
impl<T: Speak> Speak for &T {
    fn speak(&self) {
        (*self).speak();
    }
}

// Blanket implementation for &mut T
impl<T: Speak> Speak for &mut T {
    fn speak(&self) {
        (**self).speak();
    }
}

struct Human {
    name: String,
}

impl Speak for Human {
    fn speak(&self) {
        println!("Hello, my name is {}.", self.name);
    }
}

struct Introduction;

impl Introduction {
    fn introduce(&self, speaker: impl Speak) {
        println!("Let me introduce our speaker:");
        speaker.speak();
    }
}

fn main() {
    let person = Human {
        name: String::from("Jhon"),
    };
    let introduction = Introduction;
    introduction.introduce(&person);
}

References and immutability

let

By default the let keyword defines immutable variable.

    let immutable_u8_val: u8 = 200u8;
    let _borrow_immutable_u8_val: &u8 = &immutable_u8_val; // shared reference
    // cannot borrow `immutable_u8_val` as mutable, as it is not declared as mutable
    // let _borrow_mutable_u8_val: &mut u8 = &mut immutable_u8_val; // exclusive reference

let mut

The mut keyword allows to define mutable variable.

    let mut mutable_u8_val: u8 = 200u8;
    let _borrow_immutable_u8_val: &u8 = &mutable_u8_val;  // shared reference
    let borrow_mutable_u8_val: &mut u8 = &mut mutable_u8_val; // exclusive reference
    *borrow_mutable_u8_val += 1; // dereference and mutate

There are two ways to create a reference to an arbitrary memory address.

*const

    // 0xE000_E010 is a memory address
    // *const means raw pointer of immutable value
    let const_raw_ref: *const u32 = 0xE000_E010 as *const u32;
    
    // dereference the raw pointer to u32
    // it's unsafe because Rust compiler doesn't know anything about 0xE000_E010 memory address
    let immutable_val: u32 = unsafe { *const_raw_ref };
    let _borrow_immutable_val: &u32 = &immutable_val;
    
    // cannot borrow `*const_raw_ref` as mutable, as it is behind a `*const` pointer
    // that means we must use `*mut` to define the raw pointer (see below)
    // let borrow_mutable_val: &mut u32 = unsafe { &mut *const_raw_ref };
    
    // not sure that this is correct, but Rust compiler seems ok with that
    // note how we moved the `&mut` ahead of `unsafe` block
    let borrow_mutable_val_from_const_pointer: &mut u32 = &mut unsafe { *const_raw_ref };
    *borrow_mutable_val_from_const_pointer += 1; // dereference and mutate
    
    // with `mut` keyword we can turn the `*const` pointer into mutable variable
    let mut mutable_val: u32 = unsafe { *const_raw_ref };
    let borrow_mutable_val: &mut u32 = &mut mutable_val;
    *borrow_mutable_val += 1; // dereference and mutate

*mut

    // *mut means raw pointer for muttable value
    let mut_raw_ref: *mut u32 = 0xE000_E010 as *mut u32;
    // dereference the raw pointer to u32
    let borrow_mutable_val: &mut u32 = unsafe { &mut *mut_raw_ref };
    *borrow_mutable_val += 1;
    
    // all in one
    // `0xE000_E010 as *mut u32` - raw reference
    // `*(0xE000_E010 as *mut u32)` - dereference to u32
    // `&mut *(0xE000_E010 as *mut u32)` - borrow u32 to mutate
    let val: &mut u32 = unsafe { &mut *(0xE000_E010 as *mut u32) };
    *val += 1;

Functions, Closures, and their Traits

Function type

Unused function pointer

The function pointer that is not used anywhere doesn't require any memory.

fn main() {
  let a = foo::<u32>;
  println!("{}", core::mem::size_of_val(&a)); // 0
}

fn foo<T>() {}
  • foo<T> is a generic function type
  • a holds a function pointer to the generic function foo with a specified type parameter of u32

Used function pointer

The function pointer that is going to be used must have some memory occupation.

fn main() {
  let a = foo::<u32>;
  println!("{}", core::mem::size_of_val(&a)); // 0
  
  bar(foo::<u32>);
}

fn foo<T>() {}

fn bar(f: fn()) {
  println!("{}", core::mem::size_of_val(&f)); // 8
}
  • bar expects function pointer

Function type in place of Fn(), FnMut(), and FnOnce() traits

The function type implements the Fn(), FnMut(), and FnOnce() traits by default. The function pointer could be used in place of any of the function traits implementation required. That's because the function type doesn't hold any external references.

fn main() {
  baz(foo::<u32>);
}

fn foo<T>() {}

fn baz<F>(f: F)
where 
  F: Fn(),
{
  (f)()
}
  • baz expects anything that implement the Fn() trait

Fn(), FnMut(), and FnOnce() traits

The relationship between the function traits is this:

  • Fn() could be used in place of FnMut() or FnOnce()
  • FnMut() could be used in place of FnOnce()

It means if something implements the Fn() trait it could be called multiple times simultaneously as it doesn't hold any ownership or exclusive reference from the external scope, otherwise it will violent the borrowing rules. That what the function type actually is.

If something implements the FnMut() trait it also could be called multiple times, but not simultaneously as it holds some exclusive reference that couldn't be shared.

If something implements the FnOnce() trait it could be called only once as it takes an ownership from the external scope and will drop the owned value after the first execution which makes impossible the subsequent execution.

Closures

The main consumer of the function traits Fn(), FnMut(), and FnOnce() are closures.

Closure that has the Fn() trait

In its simplest form the closure doesn't take ownership or hold any external reference (non-capturing closure). That makes it seem to the compiler as something that implements the Fn() trait.

fn main() {
  let f = || (); // non-capturing closure
  baz(f);
}

fn baz<F>(f: F)
where 
  F: Fn(),
{
  (f)()
}

The non-capturing closure could be coerced to the function pointer. (only non-capturing!)

fn main() {
  let f = || (); // non-capturing closure
  baz(f);
}

fn baz(f: fn()) {
  println!("{}", core::mem::size_of_val(&f)); // 8
}

The closure that behaves like Fn() still could take the shared references to use internally as the borrowing rules do not prohibit it.

fn main() {
  let a = String::from("foo");
  let f = || {
    println!("{}", a); // captures the `a` value
  }; 
  baz(f);
}

fn baz<F>(f: F)
where 
  F: Fn(),
{
  (f)()
}

The Fn() closure could be used in place of FnMut() or FnOnce()

fn main() {
  let a = String::from("foo");
  let f = || {
    println!("{}", a);
  };
  bar(f);
  baz(f);
  bax(f);
}

fn bar<F>(f: F)
where 
  F: Fn(),
{
  (f)()
}

fn baz<F>(mut f: F)
where 
  F: FnMut(),
{
  (f)()
}

fn bax<F>(f: F)
where 
  F: FnOnce(),
{
  (f)()
}

Closure that has the FnMut() trait

The FnMut() closure holds exclusive reference and mutate external value catched by closure.

fn main() {
  let mut a = String::from("foo");
  let f = || {
    a.clear();
  };
  baz(f);
}

fn baz<F>(mut f: F)
where 
  F: FnMut(),
{
  (f)()
}

The closure with the FnMut() trait cannot be used in place of Fn() as the Fn() trait means multiple executions from the different threads simultaneously which is not allowed by the borrowin rules in case of FnMut() trait.

fn main() {
  let mut a = String::from("foo");
  let f = || {
    a.clear();
  };
  bar(f);
}

fn bar<F>(f: F)
where 
  F: Fn(),
{
  (f)()
}

Output

error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnMut`
 --> src/main.rs:3:11
  |
3 |   let f = || {
  |           ^^ this closure implements `FnMut`, not `Fn`
4 |     a.clear();
  |     - closure is `FnMut` because it mutates the variable `a` here
5 |   };
6 |   bar(f);
  |   --- the requirement to implement `Fn` derives from here

But it's allowed to use the FnMut() closure in place of FnOnce().

fn main() {
  let mut a = String::from("foo");
  let f = || {
    a.clear();
  };
  bar(f);
}

fn bar<F>(f: F)
where 
  F: FnOnce(), // changed from `Fn()`
{
  (f)()
}

Closure that has the FnOnce() trait

The most strict of the function traits FnOnce() allows to be called only once as it takes the ownership of the external value and drop it after execution.

fn main() {
  let a = String::from("foo");
  let f = || {
    drop(a); // `a` dropped here
  };
  bar(f);
}

fn bar<F>(f: F)
where 
  F: FnOnce(),
{
  (f)()
}

The FnOnce() trait cannot be substitude by any other function traits.

fn main() {
  let mut a = String::from("foo");
  let f = || {
    drop(a);
  };
  bar(f);
}

fn bar<F>(mut f: F)
where 
  F: FnMut(),
{
  (f)()
}

Output

error[E0525]: expected a closure that implements the `FnMut` trait, but this closure only implements `FnOnce`
 --> src/main.rs:3:11
  |
3 |   let f = || {
  |           ^^ this closure implements `FnOnce`, not `FnMut`
4 |     drop(a);
  |          - closure is `FnOnce` because it moves the variable `a` out of its environment
5 |   };
6 |   bar(f);
  |   --- the requirement to implement `FnMut` derives from here
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment