How can I cast `&self` to a trait object in default method?


#1

Hi! The following does not compile:

trait T {
    fn foo(&self) {
        bar(self) 
    }
}

fn bar(x: &T) {}

fn baz(x: Box<T>) {
    x.foo()
}

It says “the trait bound Self: std::marker::Sized is not satisfied” inside the default method. But if I add a Sized bound, I can’t call foo on the boxed T. Is there any way to solve this dilemma?


#2

did you want?

trait T {
    fn foo(&self) {
        bar(&self) 
    }
}

Just trying to understand.


#3

This does not work either: https://is.gd/6mT0TO :frowning:


#4

oops… trait object, I have yet to get the hang of them. I expect to learn from the competent replies. Sorry for the noize.


#5

Hi, You just need to provide a trait bound to your bar method, and things will work, as functions takes arguments which have a known size at compile time.

The following works :

use std::marker::Sized;

struct Tee;

trait T {
	fn foo(&self) {
		bar(&self)
	}
}

fn bar<T: Sized>(x: &T) {}

fn baz(x: Box<T>) {
	x.foo();
}

impl T for Tee {}

fn main() {
	let tee = Tee;
	baz(Box::new(tee));
}

#6

Yep, if just monomorphize everything it works, but I really want to use trait objects here :slight_smile:

If I manually repeat the default implementation for each impl than it works:

trait T {
    fn foo(&self); 
}

impl T for () { // generic impl like `impl<S> T for S` would also work
    fn foo(&self) {
        bar(self) 
    }
}

Why default methods are special?


#7

They aren’t special. That one works because () is sized. See my thread here:

And a related RFC Issue:

With the unsize feature, you could write:

#![feature(unsize)]
use std::marker::Unsize;

trait T {
    fn foo<'a>(&'a self) where Self: 'a + Unsize<T + 'a> {
        bar(self)
    }
}

fn bar(x: &T) {}

fn baz(x: Box<T>) {
    x.foo()
}

fn biz<V: T>(x: V) {
    x.foo()
}

But it’s rather messy, unstable, and has some weird edge cases.


#8

This may not be what you want, but adding Sized bound and implmenting T for Box<T> seems to work.

trait T {
    fn foo(&self) where Self: Sized {
        bar(self) 
    }
}

impl T for Box<T> {
}

fn bar(x: &T) {}

fn baz(x: Box<T>) {
    x.foo()
}

#9

Ouch, this DST stuff is complex.

Is there a definitive reference about them? The docs I’ve found are a bit skinny:

https://doc.rust-lang.org/beta/book/unsized-types.html
https://doc.rust-lang.org/std/marker/trait.Sized.html
https://doc.rust-lang.org/nomicon/exotic-sizes.html

This post from early 2014 is helpful, but I’d want to read something more up to date :slight_smile:

The thing that puzzles me most is how on earth can the raw pointer be fat?

trait T { fn foo(&self); }
fn main() {
    println!("ref: {} {} {}\nptr: {} {} {}", 
             std::mem::size_of::<&i32>(),
             std::mem::size_of::<&[i32]>(),
             std::mem::size_of::<&T>(),

             std::mem::size_of::<*const i32>(),
             std::mem::size_of::<*const [i32]>(),
             std::mem::size_of::<*const T>(),
    )
}

#10

Probably because - at least for the *const T - about the only reasonable thing you could do with it is to transmute it back into a &T, and then it’ll need its vtable again :slight_smile:


#11

The best I could find is this. Looks like:

trait Trait : AsTrait {
    fn foo(&self) where Self : AsTrait {
        bar(self.as_trait());
    }
}

trait AsTrait {
    fn as_trait(&self) -> &Trait;
}

impl<T : Trait + Sized> AsTrait for T {
    fn as_trait(&self) -> &Trait { self }
}

It is pretty ugly and I didn’t manage to create generic implementation of it, because compiler didn’t like

trait AsObject<Object> { ... }

trait Trait : AsObject<Trait> { ... }
//                     ^^^^^ This kind of recursion appears not to be permitted.