Reputation: 1058
Being a beginner in Rust programming I'm a bit confused by the Size trait in conjunction with generic traits. My custom trait defines a single method that takes two objects and combines them into a new one. Because the original objects expire after combination, the method should take ownership of the parameters. I also return a result with a custom error type because the combination might fail and I want to give the caller the reason for the failure.
The trait in question is:
pub trait Combinable<T> {
fn combine(self, other: T) -> CombinationResult<T>;
}
struct CombinationError;
type CombinationResult<T> = std::result::Result<dyn Combinable<T>, CombinationError>
When compiling this code I get the following error:
error[E0277]: the size for values of type `(dyn Combinable<T> + 'static)` cannot be known at compilation time
|
7 | fn combine(self, other: T) -> CombinationResult<T>;
| ^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
241 | pub enum Result<T, E> {
| - required by this bound in `std::result::Result`
|
= help: the trait `Sized` is not implemented for `(dyn Combinable<T> + 'static)`
I tried constraining the generic type parameter T to Sized
(i.e. pub trait Combinable<T: Sized>
) but that didn't work. I also tried the same at the type alias declaration, also to no avail.
Am I missing/overlooking something?
Upvotes: 2
Views: 694
Reputation: 23244
dyn Combinable<T>
can only be used behind a reference (&
, Box
, Rc
…). Since you want to transfer ownership of the combined items, you have two options:
Easiest is to just box the result: type CombinationResult<T> = std::result::Result<Box<dyn Combinable<T>>, CombinationError>;
but this adds some overhead in the form of memory allocation and indirection.
Or you can use a generic type to avoid the indirection:
pub trait Combinable<T> {
type Combined;
fn combine(self, other: T) -> CombinationResult<Self::Combined>;
}
pub struct CombinationError;
type CombinationResult<T> = std::result::Result<T, CombinationError>;
The second solution works by moving the combined items into the returned value. Here's how we would do it without generics, traits nor error handling:
struct ItemA {}
struct ItemB {}
impl ItemA {
fn combine (self, other: ItemB) -> CombinedAB {
CombinedAB { a: self, b: other }
}
}
struct CombinedAB {
a: ItemA,
b: ItemB,
}
Notice that the type returned by the combine
method depends on the type of the inputs. If we want to abstract the combine
method into a trait, we can't fix the return type when we declare the trait since we don't yet know the types that will be used in the implementation. For this reason we use an associated type, which is a way of saying: "this type will be specified by the implementation". Here's the same example using traits and generics:
pub trait Combinable<T> {
type Combined;
fn combine(self, other: T) -> Self::Combined;
}
struct ItemA {}
struct ItemB {}
struct CombinedAB {
a: ItemA,
b: ItemB,
}
impl Combinable<ItemB> for ItemA {
type Combined = CombinedAB;
fn combine (self, other: ItemB) -> CombinedAB {
CombinedAB { a: self, b: other }
}
}
Note that I used a generic type for the parameter and an associated type for the returned value. This question gives more details and explains when to use one or the other.
Of course if the combine
method should return the same concrete type for all inputs, then you can dispense with the associated type altogether:
struct Combined {}
pub trait Combinable<T> {
fn combine(self, other: T) -> Combined;
}
Upvotes: 2
Reputation: 59862
The dyn Combinable<T>
is an unsized type (since traits can be implemented by any manner of types, the size of the value is not known at compile time). This in turn means that Result<dyn Combinable<T>, ...>
is also unsized. Unsized types are fairly limited with one of those limitations being they cannot be returned by value. You can only meaningfully handle unsized types through some kind of indirection (reference, pointer, etc.)
The usual fix is to box them:
type CombinationResult<T> = std::result::Result<Box<dyn Combinable<T>>, CombinationError>
// ^^^
Upvotes: 0