I am new to Rust. My background is Java. I am trying to solve the following problem.
trait Fuel which is implemented for struct Diesel and struct Gas.trait Vehicle with Fuel generic type parameter. Vehicle is implemented for struct Car<F: Fuel> and struct Bus<F: Fuel>.Finally I want to put references to possible heterogeneous trait objects to one Vec like this:
let diesel_car = Car { fuel: Diesel {} };
let gas_car = Car { fuel: Gas {} };
let diesel_bus = Bus { fuel: Diesel {} };
let gas_bus = Bus { fuel: Gas {} };
let garage = vec![
&diesel_car,
&gas_car,
&diesel_bus,
&gas_bus
];
But I get this compile error:
error[E0308]: mismatched types
--> src/main.rs:63:9
|
63 | &gas_car,
| ^^^^^^^^ expected struct `Diesel`, found struct `Gas`
|
= note: expected type `&Car<Diesel>`
found reference `&Car<Gas>`
What should be the generic type of references in this Vec? It guess it should be something like Vec<&dyn Vehicle>. But this variant doesn't compile either since it wants to know the generic type parameter of Vehicle in advance. In Java I would just write List<Vehicle<?>. Is there anything similar in Rust?
The whole example is available in the playground.
P.S. I can obviously remove a generic parameter from Vehicle and replace it with Box<dyn Fuel>, but I would like to minimize places with dynamic dispatch.
But this variant doesn't compile either since [the compiler] wants to know the generic type parameter of
Vehiclein advance.
I think you're confusing static dispatch and dynamic dispatch here. I think you're also confusing what you're asking the compiler to do vs what you expect it to do. It very clearly sounds like you want dynamic dispatch, but you're trying to achieve it using generics, which is not possible because generics are a purely compile-time abstraction and will always produce static dispatch code. "Generics" do not exist at run-time and are not present in the final binary. If you want "run-time generics" that's what trait objects and dynamic dispatch is for.
In Java I would just write
List<Vehicle<?>>. Is there anything similar in Rust?
Yes, replace the F: Fuel generic type parameter with a dyn Fuel trait object.
P.S. I can obviously remove a generic parameter from
Vehicleand replace it withBox<dyn Fuel>, but I would like to minimize places with dynamic dispatch.
But you just asked how to do the Java equivalent in Rust and that's literally how Java solves this problem: by using dynamic dispatch. You can't have your cake and eat it too. If you want the speed of static dispatch that means also accepting the constraints of static dispatch. If those constraints are too strict for your program then you should use trait objects and dynamic dispatch.
Rust's enums are probably the closest you can get to what you want. They're a pretty good compromise between generics and trait objects, giving you (almost) the speed of the former while affording you the flexibility of the latter. Here's your example refactored to use enums:
enum Fuel {
Diesel,
Gas,
}
impl Fuel {
fn efficiency(&self) -> f64 {
match self {
Fuel::Diesel => 0.9,
Fuel::Gas => 0.8,
}
}
}
enum Vehicle {
Car(Fuel),
Bus(Fuel)
}
impl Vehicle {
fn mass(&self) -> f64 {
match self {
Vehicle::Car(_) => 1000.0,
Vehicle::Bus(_) => 5000.0,
}
}
}
fn main() {
let diesel_car = Vehicle::Car(Fuel::Diesel);
let gas_car = Vehicle::Car(Fuel::Gas);
let diesel_bus = Vehicle::Bus(Fuel::Diesel);
let gas_bus = Vehicle::Bus(Fuel::Gas);
let garage = vec![
&diesel_car,
&gas_car,
&diesel_bus,
&gas_bus
];
}
playground
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With