What is wrong with the following code? warning: the trait `Shape` cannot be made into an object

struct Square {
    l: f32,
}

pub trait Area {
    fn area(&self) -> f32
    where
        Self: Shape;
}
impl Area for Square {
    fn area(&self) -> f32 {
        &self.l * &self.l
    }
}

pub trait Shape
where
    Self: Area,
{
}

impl Shape for Square {}

fn main() {
    let _a: Vec<Box<dyn Shape>> = vec![];
}

warning: the trait `Shape` cannot be made into an object
Also warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
The above code compiles.
I don't understand why Shape is not allowed to be a trait object. It follows all the rules given here Traits - The Rust Reference
Don't have a practical application I was just playing around.

This is the full compiler error from the Playground:

warning: the trait `Shape` cannot be made into an object
  --> src/main.rs:6:8
   |
6  |     fn area(&self) -> f32
   |        ^^^^
   |
   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
   = note: for more information, see issue #51443 <https://github.com/rust-lang/rust/issues/51443>
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
  --> src/main.rs:6:8
   |
6  |     fn area(&self) -> f32
   |        ^^^^ ...because method `area` references the `Self` type in its `where` clause
...
16 | pub trait Shape
   |           ----- this trait cannot be made into an object...
   = help: consider moving `area` to another trait
   = note: `#[warn(where_clauses_object_safety)]` on by default

You cannot reference Self in trait definitions because it is not object safe. Trait objects (dyn Trait) can only be used if the trait is object safe.

Try making Area a supertrait of Shape:

pub trait Area: Shape {
    fn area(&self) -> f32;
}

And/or using a blanket impl for Shape:

impl<T: Area> Shape for T {}

Here's an updated Playground.

2 Likes

I thought both of these pieces of code are same and meant anything implementing shape must also implement area.
What is the difference between them?

I don't understand why Self is not object safe. The code given below uses Self in trait definitions but does not give any warnings. I just removed where Self:Shape for Area trait

struct Square {
    l: f32,
}

pub trait Area {
    fn area(&self) -> f32;
    
}
impl Area for Square {
    fn area(&self) -> f32 {
        &self.l * &self.l
    }
}

pub trait Shape
where
    Self: Area,
{
}

impl Shape for Square {}

fn main() {
    let _a: Vec<Box<dyn Shape>> = vec![];
}
pub trait Area {
    fn area(&self) -> f32
    where
        Self: Shape;
}

The above code means that anything can implement Area, but the area() method is only available for things implementing Shape. Here's a playground example of something implementing Area but not Shape. Area isn't considered object safe in this case because the check on whether a type implements Shape can't be made dynamic. You could make it object safe by adding a Self : Sized bound to the area() method to make it unavailable for trait objects (but then you would have a trait object with no methods in this case).

pub trait Area: Shape {
    fn area(&self) -> f32;
}

The above code means anything implementing Area must implement Shape. This can be seen in a modified version of my previous example, where it now errors on the attempt to implement Area.

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.