Allow variable to have any trait implementation - rust

I have a program which interfaces with slightly different hardware. All hardware types support the same features which can be encapsulated in a trait.
I would like to assign the hardware used based on an argument to the program.
A minimal version of what I would try to achieve is the following
trait Hardware{
...
}
impl Hardware for HardwareTypeOne{
...
}
impl Hardware for HardwareTypeTwo{
...
}
fn do_stuff_with_hw(hw &mut impl Hardware){
...
}
fn main(){
...
let hw = match args{
arg_types::hw_1 => HardwareTypeOne,
arg_types::hw_2 => HardwareTypeTwo,
};
hw.run_something();
do_stuff_with_hw(&hw)
}
This of course won't work as the compiler will complain about not knowing the size of the hw variable at compile time.
I tried implementing this using boxing
Using if/else i was able to get this to work
let hw:Box<dyn Hardware>
if args == arg_types::hw_1{
hw = Box::new(HardwareTypeOne);
}
else{
hw = Box::new(HardwareTypeTwo);
}
But running:
hw.run_somenthing()
do_stuff_with_hw(&hw)
then won't work since the type has changed. And trying to "unbox" the variable will of course give the same error, complaining about the size not being known.
How does one go about doing something like this in rust? I would like to avoid having to compile different versions of the software for different implementations.

This can work, with a few fixes:
trait Hardware {
fn run_something(&self);
}
struct HardwareTypeOne;
struct HardwareTypeTwo;
impl Hardware for HardwareTypeOne {
fn run_something(&self) {
println!("run_something type one");
}
}
impl Hardware for HardwareTypeTwo {
fn run_something(&self) {
println!("run_something type two");
}
}
fn do_stuff_with_hw(_hw: &mut (impl Hardware + ?Sized)) {
}
fn main() {
let mut hw: Box<dyn Hardware> = match true {
true => Box::new(HardwareTypeOne),
false => Box::new(HardwareTypeTwo),
};
hw.run_something();
do_stuff_with_hw(&mut *hw);
}
hw in main() must be declared mut in order to obtain a mutable reference to the value in the box.
You need to use the syntax &mut *hw to obtain a mutable reference to the value inside the box.
You need to relax the implicit Sized constraint on do_stuff_with_hw's argument type by adding the ?Sized constraint; this allows it to be invoked with a &mut dyn Hardware. (Alternatively, you can have the method take &mut dyn Hardware instead of &mut impl Hardware.)

Related

How to implement strategies selected at run time in Rust?

I would like to write a simulation algorithm in Rust that has three main parts. The first part is a struct that maintains the current state of the system and associated methods to make allowed state transitions. The second part is a simulation strategy, this tells which state transition(s) to make next (e.g. I will have a slow but accurate simulation strategy and a quick but approximate strategy). Finally, I have a way to serialise the system after a simulation step was taken (e.g. one method writes the system to csv while another one to json).
I would like to choose both the simulation strategy and the serialisation method at run time.
Coming from Python, my first try for the main simulation loop looked like this:
let mut system = System { ... };
let simulator = GillespieAlgorithm { system: &mut system, ... }; // this will be dynamic eventually
let output_formatter = CSVFormatter { system: &system, ... }; // this will be dynamic eventually
output_formatter.start();
for _ in 1..100 {
simulator.step();
output_formatter.write_current_state();
}
output_formatter.stop();
Of course, this doesn't work because I want to borrow system twice with one of them being a mutable reference.
What would be the idiomatic Rust solution to this problem? My understanding is that I should somehow attach a behaviour dynamically to System instead of passing system to other structs.
You've mentioned in the comments, that you want to keep the &mut system in your simulation. Thats fine and you can still use system, as long as you're getting it via the GillespieAlgorithm. If you're fine to pass it to the formatter by method argument rather than constructor, this might be a solution for you (playground)
struct System();
struct GillespieAlgorithm<'a> { system: &'a mut System }
struct CSVFormatter();
fn main() {
let mut system = System();
let mut simulator : Box<dyn Algorithm> = Box::new(GillespieAlgorithm { system: &mut system }); // this will be dynamic eventually
let output_formatter: Box<dyn Formatter> = Box::new(CSVFormatter());
output_formatter.start();
for _ in 1..100 {
simulator.step();
output_formatter.write_current_state(simulator.borrow_system());
}
output_formatter.stop();
}
trait Algorithm {
fn step(&mut self) {}
fn borrow_system(&self) -> &System;
}
impl<'a> Algorithm for GillespieAlgorithm<'a> {
fn step(&mut self) {}
fn borrow_system(&self) -> &System {
self.system
}
}
trait Formatter {
fn start(&self);
fn write_current_state(&self, system: &System);
fn stop(&self);
}
impl Formatter for CSVFormatter {
fn start(&self) {}
fn write_current_state(&self, _system: &System) {}
fn stop(&self) {}
}
If you don't need the entire System in the CSVFormatter, you could also return a dedicated state struct in step() and pass this to your formatter.

Why doesn't Weak::new() work when Rc::downgrade() does?

I'm creating a function which returns a Weak reference to a trait object. In situations where the object cannot be found (it's a lookup function), I want to return an empty Weak reference using Weak::new():
use std::rc::{self, Rc, Weak};
use std::cell::RefCell;
pub trait Part {}
pub struct Blah {}
impl Part for Blah {}
fn main() {
let blah = Blah {};
lookup(Rc::new(RefCell::new(blah)));
}
fn lookup(part: Rc<RefCell<Part>>) -> Weak<RefCell<Part>> {
if true {
Rc::downgrade(&part)
} else {
Weak::new()
}
}
This has the following error during compilation:
error[E0277]: the trait bound `Part + 'static: std::marker::Sized` is not satisfied in `std::cell::RefCell<Part + 'static>`
--> <anon>:19:9
|
19 | Weak::new()
| ^^^^^^^^^ within `std::cell::RefCell<Part + 'static>`, the trait `std::marker::Sized` is not implemented for `Part + 'static`
|
= note: `Part + 'static` does not have a constant size known at compile-time
= note: required because it appears within the type `std::cell::RefCell<Part + 'static>`
= note: required by `<std::rc::Weak<T>>::new`
Why is it that I can successfully create a Weak<RefCell<Part>> from Rc::downgrade() but cannot use the same type to create a new Weak reference with Weak::new()?
Is there a way for me to annotate Weak::new() to help the compiler or will I have to wrap this in an Option to let the user know the part wasn't found?
Working minimal example
The type inferred for Weak::new() is Weak<RefCell<Part>>, and the Part part can not be created because it's a trait!
That's what the Sized error is all about. The trait is not a concrete structure, it has no size known at compile time, so the compiler wouldn't know how much space to allocate.
Why is it that I can successfully create a Weak<RefCell<Part>> from Rc::downgrade()
It is because Rc<RefCell<Part>> points to a structure that is already allocated. Compiler can reference it with a trait pointer even though it doesn't know whether it's a Blah or some other implementation of the Part trait.
Is there a way for me to annotate Weak::new() to help the compiler
You can indeed annotate Weak::new(), pointing the compiler to the implementation of Part that you want instantiated, like this:
use std::rc::{Rc, Weak};
use std::cell::RefCell;
pub trait Part {}
pub struct Blah {}
impl Part for Blah {}
fn main() {
let blah = Blah {};
lookup(Rc::new(RefCell::new(blah)));
}
fn lookup(part: Rc<RefCell<Part>>) -> Weak<RefCell<Part>> {
if true {
Rc::downgrade(&part)
} else {
Weak::<RefCell<Blah>>::new()
}
}
TL;DR: Fat pointers are hard.
And therefore you need to specify the concrete type explicitly before coercion takes place:
Weak::<RefCell<Blah>>::new()
Note: if Blah takes a lot of memory, create a Zero-Sized Type Fool, implement Part for it (all functions unimplemented!()), then use Weak::<RefCell<Fool>>::new() to avoid allocating memory uselessly.
I believe that the underlying issue is simply one of implementation issue.
It does not seem unfixable, but may require quite some work to cover all corner cases.
First, let's expose the issue.
The implementation of Weak::new:
impl<T> Weak<T> {
pub fn new() -> Weak<T> {
unsafe {
Weak {
ptr: Shared::new(Box::into_raw(box RcBox {
strong: Cell::new(0),
weak: Cell::new(1),
value: uninitialized(),
})),
}
}
}
}
For homogeneity, all Shared elements are wrapping a RcBox, which contains two Cell (the counters) and the actual value.
The mere fact of building a RcBox<T> requires that the size of T be known, which is why unlike most Weak methods, T is NOT marked as : ?Sized in this impl.
Now, since the memory is left uninitialized, it is clear that it will never be used, so actually any size would have been fine.
This is supported by the fact that RcBox can actually carry unsized data, which is necessary to go from RcBox<Struct> to RcBox<Trait>, and therefore the strong and weak fields are always laid out first (only the last field can be unsized).
Thus, we would like
Allocate a RcBox<()>, which would save memory AND not require that T be Sized,
Then transmuted to RcBox<T>, whatever T is.
Alright, let's do it!
Our desired implementation will look something like this:
impl<T: ?Sized> Weak<T> {
pub fn new() -> Weak<T> {
unsafe {
Weak {
ptr: Shared::new(transmute(Box::into_raw(box RcBox {
strong: Cell::new(0),
weak: Cell::new(1),
value: (),
}))),
}
}
}
}
which utterly fails to compile.
Why? Because *mut RcBox<()> is a thin pointer, whereas *mut RcBox<T> is either a thin pointer OR a fat pointer (see raw memory representation) depending on whether T is Sized or !Sized.
Now, trait pointers can be handled (warning: contains a simplified and totally unsafe implementation of Rc) with the following implementation of Weak::new:
impl<T: ?Sized> Weak<T> {
pub fn new() -> Weak<T> {
unsafe {
let boxed = Box::into_raw(box RcBox {
strong: Cell::new(0),
weak: Cell::new(1),
value: (),
});
let ptr = if size_of::<*mut ()>() == size_of::<*mut T>() {
let ptr: *mut RcBox<T> = transmute_copy(&boxed);
ptr
} else {
let ptr: *mut RcBox<T> = transmute_copy(&TraitObject {
data: boxed as *mut (),
vtable: null_mut(),
});
ptr
};
Weak { ptr: Shared::new(ptr) }
}
}
}
However this implementation only accounts for trait pointers, and there are other kinds of fat pointers for which it would... probably completely break down.

Rc<Trait> to Option<T>?

I'm trying to implement a method that looks like:
fn concretify<T: Any>(rc: Rc<Any>) -> Option<T> {
Rc::try_unwrap(rc).ok().and_then(|trait_object| {
let b: Box<Any> = unimplemented!();
b.downcast().ok().map(|b| *b)
})
}
However, try_unwrap doesn't work on trait objects (which makes sense, as they're unsized). My next thought was to try to find some function that unwraps Rc<Any> into Box<Any> directly. The closest thing I could find would be
if Rc::strong_count(&rc) == 1 {
Some(unsafe {
Box::from_raw(Rc::into_raw(rc))
})
} else {
None
}
However, Rc::into_raw() appears to require that the type contained in the Rc to be Sized, and I'd ideally not like to have to use unsafe blocks.
Is there any way to implement this?
Playground Link, I'm looking for an implementation of rc_to_box here.
Unfortunately, it appears that the API of Rc is lacking the necessary method to be able to get ownership of the wrapped type when it is !Sized.
The only method which may return the interior item of a Rc is Rc::try_unwrap, however it returns Result<T, Rc<T>> which requires that T be Sized.
In order to do what you wish, you would need to have a method with a signature: Rc<T> -> Result<Box<T>, Rc<T>>, which would allow T to be !Sized, and from there you could extract Box<Any> and perform the downcast call.
However, this method is impossible due to how Rc is implemented. Here is a stripped down version of Rc:
struct RcBox<T: ?Sized> {
strong: Cell<usize>,
weak: Cell<usize>,
value: T,
}
pub struct Rc<T: ?Sized> {
ptr: *mut RcBox<T>,
_marker: PhantomData<T>,
}
Therefore, the only Box you can get out of Rc<T> is Box<RcBox<T>>.
Note that the design is severely constrained here:
single-allocation mandates that all 3 elements be in a single struct
T: ?Sized mandates that T be the last field
so there is little room for improvement in general.
However, in your specific case, it is definitely possible to improve on the generic situation. It does, of course, require unsafe code. And while it works fairly well with Rc, implementing it with Arc would be complicated by the potential data-races.
Oh... and the code is provided as is, no warranty implied ;)
use std::any::Any;
use std::{cell, mem, ptr};
use std::rc::Rc;
struct RcBox<T: ?Sized> {
strong: cell::Cell<usize>,
_weak: cell::Cell<usize>,
value: T,
}
fn concretify<T: Any>(rc: Rc<Any>) -> Option<T> {
// Will be responsible for freeing the memory if there is no other weak
// pointer by the end of this function.
let _guard = Rc::downgrade(&rc);
unsafe {
let killer: &RcBox<Any> = {
let killer: *const RcBox<Any> = mem::transmute(rc);
&*killer
};
if killer.strong.get() != 1 { return None; }
// Do not forget to decrement the count if we do take ownership,
// as otherwise memory will not get released.
let result = killer.value.downcast_ref().map(|r| {
killer.strong.set(0);
ptr::read(r as *const T)
});
// Do not forget to destroy the content of the box if we did not
// take ownership
if result.is_none() {
let _: Rc<Any> = mem::transmute(killer as *const RcBox<Any>);
}
result
}
}
fn main() {
let x: Rc<Any> = Rc::new(1);
println!("{:?}", concretify::<i32>(x));
}
I don't think it's possible to implement your concretify function if you're expecting it to move the original value back out of the Rc; see this question for why.
If you're willing to return a clone, it's straightforward:
fn concretify<T: Any+Clone>(rc: Rc<Any>) -> Option<T> {
rc.downcast_ref().map(Clone::clone)
}
Here's a test:
#[derive(Debug,Clone)]
struct Foo(u32);
#[derive(Debug,Clone)]
struct Bar(i32);
fn main() {
let rc_foo: Rc<Any> = Rc::new(Foo(42));
let rc_bar: Rc<Any> = Rc::new(Bar(7));
let foo: Option<Foo> = concretify(rc_foo);
println!("Got back: {:?}", foo);
let bar: Option<Foo> = concretify(rc_bar);
println!("Got back: {:?}", bar);
}
This outputs:
Got back: Some(Foo(42))
Got back: None
Playground
If you want something more "movey", and creating your values is cheap, you could also make a dummy, use downcast_mut() instead of downcast_ref(), and then std::mem::swap with the dummy.

Vector of traits (dynamic dispatch) which contains associated type (also dynamic dispatch) [duplicate]

I have a program that involves examining a complex data structure to see if it has any defects. (It's quite complicated, so I'm posting example code.) All of the checks are unrelated to each other, and will all have their own modules and tests.
More importantly, each check has its own error type that contains different information about how the check failed for each number. I'm doing it this way instead of just returning an error string so I can test the errors (it's why Error relies on PartialEq).
My Code So Far
I have traits for Check and Error:
trait Check {
type Error;
fn check_number(&self, number: i32) -> Option<Self::Error>;
}
trait Error: std::fmt::Debug + PartialEq {
fn description(&self) -> String;
}
And two example checks, with their error structs. In this example, I want to show errors if a number is negative or even:
#[derive(PartialEq, Debug)]
struct EvenError {
number: i32,
}
struct EvenCheck;
impl Check for EvenCheck {
type Error = EvenError;
fn check_number(&self, number: i32) -> Option<EvenError> {
if number < 0 {
Some(EvenError { number: number })
} else {
None
}
}
}
impl Error for EvenError {
fn description(&self) -> String {
format!("{} is even", self.number)
}
}
#[derive(PartialEq, Debug)]
struct NegativeError {
number: i32,
}
struct NegativeCheck;
impl Check for NegativeCheck {
type Error = NegativeError;
fn check_number(&self, number: i32) -> Option<NegativeError> {
if number < 0 {
Some(NegativeError { number: number })
} else {
None
}
}
}
impl Error for NegativeError {
fn description(&self) -> String {
format!("{} is negative", self.number)
}
}
I know that in this example, the two structs look identical, but in my code, there are many different structs, so I can't merge them. Lastly, an example main function, to illustrate the kind of thing I want to do:
fn main() {
let numbers = vec![1, -4, 64, -25];
let checks = vec![
Box::new(EvenCheck) as Box<Check<Error = Error>>,
Box::new(NegativeCheck) as Box<Check<Error = Error>>,
]; // What should I put for this Vec's type?
for number in numbers {
for check in checks {
if let Some(error) = check.check_number(number) {
println!("{:?} - {}", error, error.description())
}
}
}
}
You can see the code in the Rust playground.
Solutions I've Tried
The closest thing I've come to a solution is to remove the associated types and have the checks return Option<Box<Error>>. However, I get this error instead:
error[E0038]: the trait `Error` cannot be made into an object
--> src/main.rs:4:55
|
4 | fn check_number(&self, number: i32) -> Option<Box<Error>>;
| ^^^^^ the trait `Error` cannot be made into an object
|
= note: the trait cannot use `Self` as a type parameter in the supertraits or where-clauses
because of the PartialEq in the Error trait. Rust has been great to me thus far, and I really hope I'm able to bend the type system into supporting something like this!
When you write an impl Check and specialize your type Error with a concrete type, you are ending up with different types.
In other words, Check<Error = NegativeError> and Check<Error = EvenError> are statically different types. Although you might expect Check<Error> to describe both, note that in Rust NegativeError and EvenError are not sub-types of Error. They are guaranteed to implement all methods defined by the Error trait, but then calls to those methods will be statically dispatched to physically different functions that the compiler creates (each will have a version for NegativeError, one for EvenError).
Therefore, you can't put them in the same Vec, even boxed (as you discovered). It's not so much a matter of knowing how much space to allocate, it's that Vec requires its types to be homogeneous (you can't have a vec![1u8, 'a'] either, although a char is representable as a u8 in memory).
Rust's way to "erase" some of the type information and gain the dynamic dispatch part of subtyping is, as you discovered, trait objects.
If you want to give another try to the trait object approach, you might find it more appealing with a few tweaks...
You might find it much easier if you used the Error trait in std::error instead of your own version of it.
You may need to impl Display to create a description with a dynamically built String, like so:
impl fmt::Display for EvenError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} is even", self.number)
}
}
impl Error for EvenError {
fn description(&self) -> &str { "even error" }
}
Now you can drop the associated type and have Check return a trait object:
trait Check {
fn check_number(&self, number: i32) -> Option<Box<Error>>;
}
your Vec now has an expressible type:
let mut checks: Vec<Box<Check>> = vec![
Box::new(EvenCheck) ,
Box::new(NegativeCheck) ,
];
The best part of using std::error::Error...
is that now you don't need to use PartialEq to understand what error was thrown. Error has various types of downcasts and type checks if you do need to retrieve the concrete Error type out of your trait object.
for number in numbers {
for check in &mut checks {
if let Some(error) = check.check_number(number) {
println!("{}", error);
if let Some(s_err)= error.downcast_ref::<EvenError>() {
println!("custom logic for EvenErr: {} - {}", s_err.number, s_err)
}
}
}
}
full example on the playground
I eventually found a way to do it that I'm happy with. Instead of having a vector of Box<Check<???>> objects, have a vector of closures that all have the same type, abstracting away the very functions that get called:
fn main() {
type Probe = Box<Fn(i32) -> Option<Box<Error>>>;
let numbers: Vec<i32> = vec![ 1, -4, 64, -25 ];
let checks = vec![
Box::new(|num| EvenCheck.check_number(num).map(|u| Box::new(u) as Box<Error>)) as Probe,
Box::new(|num| NegativeCheck.check_number(num).map(|u| Box::new(u) as Box<Error>)) as Probe,
];
for number in numbers {
for check in checks.iter() {
if let Some(error) = check(number) {
println!("{}", error.description());
}
}
}
}
Not only does this allow for a vector of Box<Error> objects to be returned, it allows the Check objects to provide their own Error associated type which doesn't need to implement PartialEq. The multiple ases look a little messy, but on the whole it's not that bad.
I'd suggest you some refactoring.
First, I'm pretty sure, that vectors should be homogeneous in Rust, so there is no way to supply elements of different types for them. Also you cannot downcast traits to reduce them to a common base trait (as I remember, there was a question about it on SO).
So I'd use algebraic type with explicit match for this task, like this:
enum Checker {
Even(EvenCheck),
Negative(NegativeCheck),
}
let checks = vec![
Checker::Even(EvenCheck),
Checker::Negative(NegativeCheck),
];
As for error handling, consider use FromError framework, so you will able to involve try! macro in your code and to convert error types from one to another.

How to have a reference to a trait and still use original struct?

My goal is to have a reference counted struct which is referred as a trait in one context and by its concrete type in another. Best explained in code:
#![feature(box_syntax)]
use std::rc::Rc;
use std::cell::RefCell;
trait Employee {
fn be_managed(&mut self);
}
struct Human;
impl Human {
fn be_human(&mut self) {
println!("I'm just a human who needs a mutable self sometimes");
}
}
impl Employee for Human {
fn be_managed(&mut self) {
println!("Off to the salt mines");
}
}
struct Manager {
my_employee: Rc<RefCell<Box<Employee + 'static>>>, //'
}
fn main() {
let mut human1 = Rc::new(RefCell::new(box Human as Box<Employee>));
let manager1 = Manager {
my_employee: human1.clone(), // This works due to cast above
};
manager1.my_employee.borrow_mut().be_managed();
human1.borrow_mut().be_human(); // But we can't be human anymore
let mut human2 = Rc::new(RefCell::new(box Human));
let manager2 = Manager {
my_employee: human2.clone(), // This doesn't work
};
manager2.my_employee.borrow_mut().be_managed();
human2.borrow_mut().be_human();
}
I want the Manager to be able to have any struct implementing the Employee trait as my_employee, but other references should still be able to call other methods on the original object, ie be_human.
Right now I'm getting the following errors from the above code:
src/main.rs:37:25: 37:35 error: type `core::cell::RefMut<'_, Box<Employee>>` does not implement any method in scope named `be_human`
src/main.rs:37 human1.borrow_mut().be_human(); // But we can't be human anymore
^~~~~~~~~~
src/main.rs:44:22: 44:36 error: mismatched types:
expected `alloc::rc::Rc<core::cell::RefCell<Box<Employee + 'static>>>`,
found `alloc::rc::Rc<core::cell::RefCell<Box<Human>>>`
(expected trait Employee,
found struct `Human`) [E0308]
src/main.rs:44 my_employee: human2.clone(), // This doesn't work
^~~~~~~~~~~~~~
What's the right approach in this situation?
Disclaimer: in this answer I will assume that you are willingly NOT using an enum because you want Employee to be open.
This issue comes up in about every single language that uses dynamic polymorphism, and the traditional answer is the Visitor Pattern.
It is not exactly ideal, though, because of the dependencies it introduces, so if necessary you can use the Acyclic Visitor Pattern; however I advise that you start with a bare bone visitor before delving further.
trait EmployeeVisitor {
fn visit_employee(&self, e: &Employee);
fn visit_human(&self, h: &Human);
}
trait Employee {
fn accept(&self, v: &EmployeeVisitor) {
v.visit_employee(self);
}
}
impl Employee for Human {
fn accept(&self, v: &EmployeeVisitor) {
v.visit_human(self);
}
}
This is the traditional "every problem can be solved with a layer of indirection", and it incurs the traditional issue of bringing another layer of indirection.

Resources