After reading this answer to "Vector of objects belonging to a trait", it looks like Rust does automatic unboxing. Is this the case?
My code doesn't compile and I don't understand how that answer's code could compile.
What is the correct way to unbox the elements of a polymorphic vector, one containing boxed traits?
I've read Rust by Example and the Box
documentation and I can't see any method that looks like unbox()
.
My code is:
trait HasArea {
fn area(&self) -> f64;
}
struct Circle {
x: f64,
y: f64,
radius: f64,
}
impl HasArea for Circle {
fn area(&self) -> f64 {
std::f64::consts::PI * (self.radius * self.radius)
}
}
struct Square {
x: f64,
y: f64,
side: f64,
}
impl HasArea for Square {
fn area(&self) -> f64 {
self.side * self.side
}
}
fn print_area<T: HasArea>(shape: T) {
println!("This shape has an area of {}", shape.area());
}
fn main() {
let c = Circle {
x: 0.0f64,
y: 0.0f64,
radius: 1.0f64,
};
let s = Square {
x: 0.0f64,
y: 0.0f64,
side: 1.0f64,
};
print_area(c);
print_area(s);
let vec: Vec<Box<HasArea>> = Vec::new();
vec.push(Box::new(c));
vec.push(Box::new(s));
for x in vec {
print_area(x)
}
}
My error is:
Compiling rustgraph v0.1.0 (file:///home/chris/lunch/rustgraph)
error[E0277]: the trait bound `Box<HasArea>: HasArea` is not satisfied
--> src/main.rs:54:9
|
54 | print_area(x)
| ^^^^^^^^^^ the trait `HasArea` is not implemented for `Box<HasArea>`
|
= note: required by `print_area`
As an alternative to what E_net4 suggested, instead of boxing your traits you could use a
Vec
with references to make it work:To answer your direct question:
You cannot. Once something has been boxed and had the concrete type erased, that's it. A
Box<SomeTrait>
cannot be made back into aSomeConcreteType
, because nothing knows what that concrete type is.To solve the problem in the code... check the error message again:
That's because a reference to a trait (or a box of a trait) does not implement that trait!
To allow your program to compile and run as you originally wrote it, you only need to implement the trait for boxes, and we might as well do references too:
This allows your fixed-up main to run:
Here, we pass a reference of
c
ands
toprint_area
, to avoid transferring ownership. We also use thevec!
macro to construct the vector with much less ceremony.You can dereference it like
print_area(*x)
, but it won't work for other reasons: theSized
bound for theprint_area
argument. Your function needs to know the size of its arguments.You have other problems in your code: you are trying to push into an immutable vector and you are trying to box moved values. These were moved after you used it in
print_area()
.My opinion is that it would be easier to make
print_area
a method which takes an immutable reference. This will work as you expected.Not as automatic as you might think. In fact, you were looking for an
unbox
method whileBox<T>
implementsDeref
for targetT
. That means you should either callas_ref()
or rely onDeref
coercion. Note that aT
is not possible for unsized types, and since you are relying on polymorphic types, the consumer function will have to accept a reference.I took the liberty of fixing
main
andprint_area
to make it work. The vector was also improperly declared as immutable.