I have a set of types that implements a given trait, I want to get a concrete type object from a string name, strangely it works when my match
returns Box<dyn Trait>
but doesn't when I wrap it in a Result
.
Given this trait and types:
trait Shape {
fn edges(&self) -> u8;
}
struct Triangle {}
impl Shape for Triangle {
fn edges(&self) -> u8 {
3
}
}
struct Rectangle {}
impl Shape for Rectangle {
fn edges(&self) -> u8 {
4
}
}
This works:
fn get_shape_edges(name: &str) -> Result<u8, &str> {
let shape: Box<dyn Shape> = match name {
"triangle" => Box::new(Triangle {}),
"rectangle" => Box::new(Rectangle {}),
_ => panic!("Bad value"),
};
Ok(shape.edges())
}
However this doesn't:
fn get_shape_edges(name: &str) -> Result<u8, &str> {
let shape: Box<dyn Shape> = match name {
"triangle" => Ok(Box::new(Triangle {})),
"rectanble" => Ok(Box::new(Rectangle {})),
_ => Err("bad value")
}?;
Ok(shape.edges())
}
The error:
error[E0308]: `match` arms have incompatible types
--> src/main.rs:24:24
|
22 | let shape: Box<dyn Shape> = match name {
| _________________________________-
23 | | "triangle" => Ok(Box::new(Triangle {})),
| | ------------------------- this is found to be of type `Result<Box<Triangle>, _>`
24 | | "rectanble" => Ok(Box::new(Rectangle {})),
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Triangle`, found struct `Rectangle`
25 | | _ => Err("bad value")
26 | | }?;
| |_____- `match` arms have incompatible types
|
= note: expected type `Result<Box<Triangle>, _>`
found enum `Result<Box<Rectangle>, _>`
This is just an example of course, in my code I want to use the latter case to handle the error.
Why the latter case doesn't work?
You need to cast the Box
to Box<dyn Shape>
, otherwise the compiler is not smart enough (still) to elide the type:
fn get_shape_edges(name: &str) -> Result<u8, &str> {
let shape: Box<dyn Shape> = match name {
"triangle" => Ok(Box::new(Triangle {}) as Box<dyn Shape>),
"rectanble" => Ok(Box::new(Rectangle {}) as Box<dyn Shape>),
_ => Err("bad value")
}?;
Ok(shape.edges())
}
Btw, since you are already building a result, you could just map the edges
to the original built one:
fn get_shape_edges(name: &str) -> Result<u8, &str> {
let shape = match name {
"triangle" => Ok(Box::new(Triangle {}) as Box<dyn Shape>),
"rectanble" => Ok(Box::new(Rectangle {}) as Box<dyn Shape>),
_ => Err("bad value")
};
shape.map(|s| s.edges())
}