Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'static: std::marker::Sized` is not satisfied - do I need to Box?

I'm about to give a trait as argument to store it through a contructor method called new.

The trait for a struct type is given as argument here:

renderer.rs

use super::shapes::Shape;

pub struct Renderer;

impl Renderer{
    pub fn set_shape<T : Shape>(&self, shape: T) -> T::Builder{
        T::Builder::new(shape)
    }
}

then the contructor of the Builder specified by the associated type will be called

shapebuilder.rs

use super::shapes::Shape;
use super::shapes::Rectangle;

pub trait ShapeBuilder{
    fn new<T:Shape>(shape: T) -> Self;
}

pub struct RectangleBuilder{
    shape: Shape<Builder=RectangleBuilder>
}

impl ShapeBuilder for RectangleBuilder{
    fn new<T:Shape>(shape: T) -> Self{
        RectangleBuilder{
            shape: shape as Rectangle
        }
    }
}

at this point I already want to point out the compiler output

compiler_output

error[E0277]: the trait bound `shapes::Shape<Builder=shapebuilder::RectangleBuilder> + 'static: std::marker::Sized` is not satisfied
  --> shapes.rs:14:6
   |
14 | impl Shape for Rectangle{
   |      ^^^^^
   |
   = note: `shapes::Shape<Builder=shapebuilder::RectangleBuilder> + 'static` does not have a constant size known at compile-time
   = note: required because it appears within the type `shapebuilder::RectangleBuilder`
   = note: required by `shapes::Shape`

error: aborting due to previous error

I found similar questions here on SO which told something about Boxing. I tried to Box every parameter type to solve the issue. Boxed it like this shape: Box<T>. No success. Do I need to box at all? I understand the issue that the compiler is not able to resolve the size of a trait since the specific/concrete struct types can have different sizes depending on their field/properties. Still I'm not able to find a solution. Hope it's trivial.


NOT involed modules (my opinion) listed for completeness

shapes.rs

use super::shapebuilder::ShapeBuilder;
use super::shapebuilder::RectangleBuilder;

pub trait Shape{
    type Builder: ShapeBuilder;
}

#[derive(Clone, Copy)]
pub struct Rectangle{
    pub height: usize,
    pub width: usize,
}

impl Shape for Rectangle{
    type Builder = RectangleBuilder;
}

lib.rs

pub mod renderer;
mod shapes;
mod shapebuilder;
like image 911
xetra11 Avatar asked Sep 10 '16 08:09

xetra11


1 Answers

Well, the compiler is not really pointing at the source of the error. The problem is here:

pub struct RectangleBuilder {
    shape: Shape<Builder=RectangleBuilder>
    //     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this is an unsized type!
}

Shape is a trait, and using it as a type yields an unsized type. We could box it to fix this error:

pub struct RectangleBuilder {
    shape: Box<Shape<Builder=RectangleBuilder>>
}

But then, what do we do about the cast here?

impl ShapeBuilder for RectangleBuilder {
    fn new<T: Shape>(shape: T) -> Self {
        RectangleBuilder {
            shape: shape as Rectangle
            //     ^^^^^^^^^^^^^^^^^^ can't cast a generic type!
        }
    }
}

If RectangleBuilder will indeed be ready to accept any Shape whose Builder is RectangleBuilder, then let's remove the cast and add the appropriate constraints where necessary.

pub mod renderer {
    use super::shapes::Shape;
    use super::shapebuilder::ShapeBuilder;

    pub struct Renderer;

    impl Renderer {
        pub fn set_shape<T: Shape + 'static>(&self, shape: T) -> T::Builder {
            T::Builder::new(shape)
        }
    }
}

mod shapes {
    use super::shapebuilder::ShapeBuilder;
    use super::shapebuilder::RectangleBuilder;

    pub trait Shape {
        type Builder: ShapeBuilder;
    }

    #[derive(Clone, Copy)]
    pub struct Rectangle {
        pub height: usize,
        pub width: usize,
    }

    impl Shape for Rectangle {
        type Builder = RectangleBuilder;
    }
}

mod shapebuilder {
    use super::shapes::Shape;

    pub trait ShapeBuilder: Sized {
        fn new<T: Shape<Builder=Self> + 'static>(shape: T) -> Self;
    }

    pub struct RectangleBuilder {
        shape: Box<Shape<Builder=RectangleBuilder> + 'static>,
    }

    impl ShapeBuilder for RectangleBuilder {
        fn new<T: Shape<Builder=Self> + 'static>(shape: T) -> Self {
            RectangleBuilder {
                shape: Box::new(shape)
            }
        }
    }
}

The 'static bound puts a limit on the references that can be stored in a particular Shape instance. 'static means that the implementations cannot contain references, unless they have the 'static lifetime.

However, if you'll need to use Rectangle's fields in RectangleBuilder, then RectangleBuilder should only accept Rectangles, rather than any shape. We can use associated types again to express this.

pub mod renderer {
    use super::shapes::Shape;
    use super::shapebuilder::ShapeBuilder;

    pub struct Renderer;

    impl Renderer {
        pub fn set_shape<T: Shape>(&self, shape: T) -> T::Builder {
            T::Builder::new(shape)
        }
    }
}

mod shapes {
    use super::shapebuilder::ShapeBuilder;
    use super::shapebuilder::RectangleBuilder;

    pub trait Shape {
        type Builder: ShapeBuilder<Shape=Self>;
    }

    #[derive(Clone, Copy)]
    pub struct Rectangle {
        pub height: usize,
        pub width: usize,
    }

    impl Shape for Rectangle {
        type Builder = RectangleBuilder;
    }
}

mod shapebuilder {
    use super::shapes::Shape;
    use super::shapes::Rectangle;

    pub trait ShapeBuilder: Sized {
        type Shape: Shape + ?Sized;

        fn new(shape: Self::Shape) -> Self;
    }

    pub struct RectangleBuilder {
        shape: Rectangle,
    }

    impl ShapeBuilder for RectangleBuilder {
        type Shape = Rectangle;

        fn new(shape: Self::Shape) -> Self {
            RectangleBuilder {
                shape: shape
            }
        }
    }
}

In ShapeBuilder, we've added a Shape associated type that specifies what type of Shape each ShapeBuilder will operate on. ShapeBuilder::new now uses this associated type, instead of a type parameter, to specify the type of its operand. Note that the + ?Sized bound is needed because otherwise there's an implicit + Sized bound, and Rust complains that Shape doesn't imply Sized. Another way to fix this is to add : Sized to Shape's definition.

pub trait Shape: Sized {
    type Builder: ShapeBuilder<Shape=Self>;
}
like image 152
Francis Gagné Avatar answered Oct 21 '22 03:10

Francis Gagné