1
votes

Given the following code:

trait Function {
    fn filter (&self);
}

#[derive(Debug, Copy, Clone)]
struct Kidney {}

impl Function for Kidney {
    fn filter (&self)  {
        println!("filtered");
    }  
}

fn main() {
    let k = Kidney {};
    let f: &Function = &k;
    //let k1 = (*f);   //--> This gives a "size not satisfied" error
    (*f).filter();     //--> Works; what exactly happens here?
}

I am not sure why it compiles. I was expecting the last statement to fail. I guess I have overlooked some fundamentals while learning Rust, as I am failing to understand why dereferencing a trait (that lives behind a pointer) should compile.

Is this issue similar to the following case?

let v = vec![1, 2, 3, 4];
//let s: &[i32] = *v;
println!("{}", (*v)[0]);

*v gives a slice, but a slice is unsized, so again it is not clear to me how this compiles. If I uncomment the second statement I get

   |     let s:&[i32]= *v;
   |                   ^^
   |                   |
   |                   expected &[i32], found slice
   |                   help: consider borrowing here: `&*v`
   |
   = note: expected type `&[i32]`
              found type `[{integer}]`

Does expected type &[i32] mean "expected a reference of slice"?

2
&[T] is a slice; [T] is an array.ljedrz

2 Answers

3
votes

Dereferencing a trait object is no problem. In fact, it must be dereferenced at some point, otherwise it would be quite useless.

let k1 = (*f); fails not because of dereferencing but because you try to put the raw trait object on the stack (this is where local variables live). Values on the stack must have a size known at compile time, which is not the case for trait objects because any type could implement the trait.

Here is an example where a structs with different sizes implement the trait:

trait Function {
    fn filter (&self);
}

#[derive(Debug, Copy, Clone)]
struct Kidney {}

impl Function for Kidney {
    fn filter (&self)  {
        println!("filtered");
    }  
}

#[derive(Debug, Copy, Clone)]
struct Liver {
    size: f32
}

impl Function for Liver {
    fn filter (&self)  {
        println!("filtered too!");
    }  
}

fn main() {
    let k = Kidney {};
    let l = Liver {size: 1.0};

    let f: &Function;
    if true {
        f = &k;
    } else {
        f = &l;
    }

    // Now what is the size of *f - Kidney (0 bytes) or Liver (4 bytes)?
}

(*f).filter(); works because the temporarily dereferenced object is not put on the stack. In fact, this is the same as f.filter(). Rust automatically applies as many dereferences as required to get to an actual object. This is documented in the book.

What happens in the second case is that Vec implements Deref to slices, so it gets all methods implemented for slices for free. *v gives you a dereferenced slice, which you assign to a slice. This is an obvious type error.

1
votes

Judging by the MIR produced by the first piece of code, (*f).filter() is equivalent to f.filter(); it appears that the compiler is aware that since filter is a method on &self, dereferencing it doesn't serve any purpose and is omitted altogether.

The second case, however, is different, because dereferencing the slice introduces bounds-checking code. In my opinion the compiler should also be able to tell that this operation (dereferencing) doesn't introduce any meaningful changes (and/or that there won't be an out-of-bounds error) and treat it as regular slice indexing, but there might be some reason behind this.