The goal is to have an object (callback_handler) implementing the traits A, B and C that can be passed to different functions that expect a trait object of type A or B, for instance.
This requires callback_handler, and the trait objects to be protected by the same Mutex (since, e.g., trait A expects &mut self). Furthermore, for this setup to work, this Mutex must be wrapped in an Arc or Rc:
use std::sync::{Arc, Mutex};
trait A {
fn a(&mut self) {}
}
trait B {}
trait C: A + B {}
struct Foo {}
impl A for Foo {}
impl B for Foo {}
impl C for Foo {}
fn register_for_A(callback_handler: Arc<Mutex<Box<dyn A>>>) {
// Register for callbacks defined in `A`.
// Store the passed handler somewhere.
}
fn register_for_B(callback_handler: Arc<Mutex<Box<dyn B>>>) {
// Register for callbacks defined in `B`.
// Store the passed handler somewhere.
}
fn main() {
let callback_handler = Arc::new(Mutex::new(Box::new(Foo{})));
// Register for callbacks using different trait objects of the same "origin" object.
// For this to be safe, all objects must be protected by the same ("shared") mutex.
// !!! This will not work since there must be some steps to cast this to the right type. !!!
register_for_A(Arc::clone(&callback_handler));
register_for_B(Arc::clone(&callback_handler));
// We can still use `callback_handler` to call methods on Foo ourself
}
Now, the question is, how can the origin object callback_hanlder of type Arc<Mutex<Box<Foo>>> be casted/converted to an Arc<Mutex<Box<dyn A>>> and Arc<Mutex<Box<dyn B>>>?
While I don't see a technical reason why this should not be possible, I don't know how to perform such a conversion and whether it is doable. While one solution would be using the Any trait, I was hoping that there is a solution that retains compile-time type safety for functions register_for_A and register_for_B.
You're getting tripped up by your double use of smart pointers Box + Arc you can simply cast an Arc<Mutex<Foo>> to an Arc<Mutex<dyn A>> using as, there is really no need to additionally Box it:
use std::sync::{Arc, Mutex};
trait A {
fn a(&mut self) {}
}
trait B {}
struct Foo {}
impl A for Foo {}
impl B for Foo {}
fn register_for_A(callback_handler: Arc<Mutex<dyn A>>) {
// Register for callbacks defined in `A`.
// Store the passed handler somewhere.
}
fn register_for_B(callback_handler: Arc<Mutex<dyn B>>) {
// Register for callbacks defined in `B`.
// Store the passed handler somewhere.
}
fn main() {
let callback_handler = Arc::new(Mutex::new(Foo{}));
// Register for callbacks using different trait objects of the same "origin" object.
// For this to be safe, all objects must be protected by the same ("shared") mutex.
// !!! This will not work since there must be some steps to cast this to the right type. !!!
register_for_A(Arc::clone(&callback_handler) as _);
register_for_B(Arc::clone(&callback_handler) as _);
// We can still use `callback_handler` to call methods on Foo ourself
}
Related
I have design issue that I would like solve with safe Rust that I haven't been able to find a viable solution. I can't use a RefCell because you can't get a & reference to the data, only Ref / RefMut.
Here is a simplified example with irrelevant fields / methods removed
use std::cell::RefCell;
use std::rc::Rc;
struct LibraryStruct {}
impl LibraryStruct {
fn function(&self, _a: &TraitFromLibrary) {}
}
trait TraitFromLibrary {
fn trait_function(&self, library_struct: LibraryStruct);
}
// I don't want to copy this, bad performance
struct A {
// fields...
}
impl TraitFromLibrary for A {
fn trait_function(&self, library_struct: LibraryStruct) {
// custom A stuff
}
}
// B manipulates A's in data
struct B {
data: Vec<A>,
}
struct C {
// This type doesn't have to be & for solution. C just needs immutable access
a: Rc<RefCell<A>>,
}
impl<'a> TraitFromLibrary for C {
fn trait_function(&self, library_struct: LibraryStruct) {
// custom C stuff
// Takes generic reference &, this is why Ref / RefCell doesn't work
library_struct.function(&self.a.borrow());
}
}
// B and C's constructed in Container and lifetime matches Container
// Container manipulates fields b and c
struct Container {
b: B,
c: Vec<C>,
}
fn main() {}
I would be able to solve this with Rc<RefCell<A>> but I am being restricted from the library requiring &A.
This produces the error:
error[E0277]: the trait bound `std::cell::Ref<'_, A>: TraitFromLibrary` is not satisfied
--> src/main.rs:33:33
|
33 | library_struct.function(&self.a.borrow());
| ^^^^^^^^^^^^^^^^ the trait `TraitFromLibrary` is not implemented for `std::cell::Ref<'_, A>`
|
= note: required for the cast to the object type `TraitFromLibrary`
If a function has an argument which is a of type &A, then you can call it with a reference to any type that dereferences to A, which includes things like &Ref<A>. The concept of one type dereferencing to another is captured by the Deref trait. This is also the reason why a function that accepts &str can be called with &String (String: Deref<Target = str>).
So, if you keep a as a Rc<RefCell<A>>, you can fix your code quite easily like this:
library_struct.function(&*self.a.borrow());
Note that this dereferences A and then reborrows it so that it can be coerced to a trait object.
I have the following 2 traits: Org, Capability.
#![feature(specialization)]
trait Org {}
struct OrgV1 {}
impl Org for OrgV1 {...}
struct OrgV2 {}
impl Org for OrgV2 {...}
trait Capability {}
struct CapV1 {}
impl Capability for CapV1 {...}
struct CapV2 {}
impl Capability for CapV2 {...}
// Capability has a generic associated .ensure method that is able to handle
// any combination of the concrete types of Org and Capability.
// It is implemented with rust's specialization feature: https://rust-lang.github.io/rfcs/1210-impl-specialization.html
trait CapabilityService<O: Org> where Self: Capability {
fn ensure(self, org_config: Arc<O>) -> Result<bool>
}
impl<O: Org, C: Capability> CapabilityService<O> for C {
default fn ensure(self, org_config: Arc<O>) -> Result<bool> {
... // not so important logic
return Ok(true)
}
}
fn main() {
...
// assume I have the following 2 variables I got in runtime by serde's deserealization:
// org_trait: instance Org trait object
// capability_trait: instance Capability trait object
// Given that there is .ensure that can handle any combination of the concrete repo and org
// is there a way in rust to downcast to a generic concrete type, aka:
let concrete_org = org_trait.as_any().downcast_ref::<T: Org>().unwrap()
let concrete_capability = capability_trait.as_any().downcast_ref::<T: Capability>().unwrap()
// where concrete_org and concrete_capability are some concrete types of Org,
// and Capability respectively.
let result = concrete_capability.ensure(concrete_org)
}
I am aware that I could do something like this:
{
if let Some(org) = org_trait.as_any().downcast_ref::<OrgV1>() {
if Some(cap) = cap_trait.as_any().downcast_ref::<CapV1>() {
cap.ensure(org)
}
...
} else Some(org) = org_trait.as_any().downcast_ref::<OrgV2>() {
...
}
...
}
But it seems like a nightmare to handle once there are more Org/Cap implementations(in my actual case, I have 3 traits that ensure works on, which I didn't include to make the picture a bit clear)
Is it possible to downcast trait to a generic concrete type, given the constraints(since we know that .ensure would be able to be called no matter what the concrete types are)? I would like to avoid writing my own long chain of nested if let Somes if possible
Thanks all. It seems like I have misunderstood how rust's specialization feature works. Specifically, my thought process was opposite of the note mentioned on the RFCs page: This default impl does not mean that Add is implemented for all Clone data, but just that when you do impl Add and Self: Clone, you can leave off add_assign
I have this rust code playground:
use itertools::{repeat_n,RepeatN};
pub trait MyIterator: Iterator {
fn fill1(elem: Self::Item, n1: usize) -> RepeatN<Self::Item>
where
Self::Item: Clone,
{
repeat_n(elem, n1)
}
}
My Problem is that I can't call this method because rustc can't infer the type.
// error[E0284]: type annotations needed
// let r: RepeatN<char> = MyIterator::fill1('a', 5);
// ^^^^^^^^^^^^^^^^^ cannot infer type
// note: cannot satisfy `<_ as Iterator>::Item == _`
let r: RepeatN<char> = MyIterator::fill1('a', 5);
I tried this but it doesn't compile:
// error[E0229]: associated type bindings are not allowed here
let r: RepeatN<char> = MyIterator::<Item=char>::fill1('a', 5);
How can I specify the type of Item in this call? Or is a function outside of the trait (like itertools::repeat_n) the best way here?
Well, you haven't implemented the trait for any types, so no fill1 function actually exists.
If you implement MyIterator for some type that implements Iterator<Item = char>, for example std::iter::Empty<char>, then you'll be able to call fill1 through that type.
use std::iter::Empty;
impl MyIterator for Empty<char> {}
fn main() {
let r: RepeatN<char> = Empty::fill1('a', 5);
}
This is, however, pointless. You will note that Empty plays no role in the actual function definition - it could have been any iterator. Furthermore there is no sensible generalization of this behavior to other Self types. This is why itertools::repeat_n is not a member of the Itertools trait: it is nonsense as a trait function because it is not a generalization of behavior shared by multiple iterator types.
In Rust, unlike some other languages, not everything has to be a member of a class. If you merely want to put fill1 and related things in a common namespace, simply use a module, which is the most basic unit of code organization.
mod my_iterator {
use itertools::{repeat_n, RepeatN};
fn fill1<T>(elem: T, n1: usize) -> RepeatN<T>
where
T: Clone,
{
repeat_n(elem, n1)
}
}
I am trying to call a function which takes a pointer to a Mutex of some trait object, and I want to be able to implement the trait for the Mutex of that trait object to allow treating the Mutex as an instance of the trait object for abstraction.
To give an example of this, imagine an Event listener setup as such:
use std::sync::{Arc, Mutex, Weak};
// Define a simple event
trait Event: Send + Sync + 'static {}
impl Event for String {}
// Define the listener interface
trait Listener<E: Event> {
fn notify(&self, event: &E);
}
// Extend the listener interface to listenrs wrapped by a mutex
impl<E: Event> Listener<E> for Mutex<Listener<E>> {
fn notify(&self, event: &E) {
self.lock().unwrap().notify(event);
}
}
// Contrived thing to listen for messages
struct Console;
impl Listener<String> for Console {
fn notify(&self, event: &String) {
println!("{}", event);
}
}
// Simple function which may be called asynchronously and then sends a message
// when it is complete
fn do_stuff(l: Arc<Listener<String>>) {
// Would normally cast to a Weak<...> and then store in a list of listneners
// For some sort of object
let m = String::from("I did stuff!");
l.notify(&m);
}
fn main() {
let l: Arc<Mutex<Console>> = Arc::new(Mutex::new(Console));
let t1 = Arc::clone(&l) as Arc<Mutex<Listener<String>>>; //this part is ok
// Here is where we run into issues... This *should* be equvlient to
// do_stuff(t1), but with the corercion explicit
let t2 = Arc::clone(&t1) as Arc<Listener<String>>;
do_stuff(t2);
// This is a simple, working example of it interpreting a Mutex<Listener<E>>
// as just a Listener<E>
let m = String::from("Somthing else...");
(l as Arc<Mutex<Listener<String>>>).notify(&m);
}
(Playground)
The issue is:
error[E0277]: the trait bound `Listener<std::string::String>: std::marker::Sized` is not satisfied in `std::sync::Mutex<Listener<std::string::String>>`
--> src/main.rs:45:14
|
45 | let t2 = Arc::clone(&t1) as Arc<Listener<String>>;
| ^^^^^^^^^^^^^^^ `Listener<std::string::String>` does not have a constant size known at compile-time
|
= help: within `std::sync::Mutex<Listener<std::string::String>>`, the trait `std::marker::Sized` is not implemented for `Listener<std::string::String>`
= note: required because it appears within the type `std::sync::Mutex<Listener<std::string::String>>`
= note: required for the cast to the object type `Listener<std::string::String>`
Why this is the case? Since an Arc is a pointer to data, from my understanding, it should be able to point to a Listener<String> which happens to be a Listener<Mutex<String>>.
I see at least two ways to avoid this, the first is to simply impl Listener<String> for Mutex<Listener<String>>, however, in the actual code this may require interdependence which should be avoided because the trait can only be implemented where the trait or structure is defined (and Mutex is not defined in my code).
The second is to move the Mutex into the Listener object so the caller does not need to cast it at all. This would work, and may be a better solution. Despite this, I am curious as to either why the casting proposed does not work and or what can be changed to make it work.
Since an Arc is a pointer to data, from my understanding, it should be able to point to a Listener<String>
Yes, that's true. I believe your problem is that you are (accidentally?) requiring that you have a Mutex<Listener<String>> at some point. This is not valid to have because the value inside the Mutex isn't behind a pointer, thus making the whole type unsized.
It's fine to have an Arc<Mutex<Listener<String>>> though.
Instead, I'd implement the trait for a Mutex of any kind that implements the same trait. I'd do the same for references and Boxed trait objects of the trait as well. In all cases, I'd remove the Sized bound to allow for trait objects:
use std::sync::{Arc, Mutex};
trait Event: Send + Sync + 'static {}
impl Event for String {}
trait Listener<E: Event> {
fn notify(&self, event: &E);
}
impl<L, E> Listener<E> for Mutex<L>
where
L: ?Sized + Listener<E>,
E: Event,
{
fn notify(&self, event: &E) {
self.lock().unwrap().notify(event);
}
}
impl<'a, L, E> Listener<E> for &'a L
where
L: ?Sized + Listener<E>,
E: Event,
{
fn notify(&self, event: &E) {
(**self).notify(event);
}
}
struct Console;
impl Listener<String> for Console {
fn notify(&self, event: &String) {
println!("{}", event);
}
}
fn do_stuff(l: Arc<Listener<String>>) {
let m = String::from("I did stuff!");
l.notify(&m);
}
fn main() {
let l: Arc<Mutex<Console>> = Arc::new(Mutex::new(Console));
let l2 = Arc::clone(&l) as Arc<Listener<String>>;
let l3 = Arc::clone(&l) as Arc<Listener<String>>;
do_stuff(l);
do_stuff(l2);
let m = String::from("Something else...");
l3.notify(&m);
}
I have a struct that contains a reference and so it has a lifetime parameter. I'd like to pass around the function pointer of a method of this struct. Later, I will call that function with an instance of the struct. I ran into snags while trying to store the function pointer, eventually finding this solution:
struct Alpha<'a> { a: &'a u8 }
impl<'a> Alpha<'a> {
fn alpha(&self) -> u8 { *self.a }
}
struct Try1(fn(&Alpha) -> u8);
struct Try2(for<'z> fn(&Alpha<'z>) -> u8);
struct Try3<'z>(fn(&Alpha<'z>) -> u8);
fn main() {
Try1(Alpha::alpha); // Nope
Try2(Alpha::alpha); // Nope
Try3(Alpha::alpha);
}
Unfortunately, this solution doesn't work for my real case because I want to implement a trait that has its own notion of lifetimes:
trait Zippy {
fn greet<'a>(&self, &Alpha<'a>);
}
impl<'z> Zippy for Try3<'z> {
fn greet<'a>(&self, a: &Alpha<'a>) { println!("Hello, {}", self.0(a)) }
}
Produces the error:
error: mismatched types:
expected `&Alpha<'z>`,
found `&Alpha<'a>`
I feel that I shouldn't need to tie the lifetime of my struct Try3 to the lifetime of the parameter of the function pointer, but the compiler must be seeing something I'm not.
Unfortunately, the function alpha implemented on the struct Alpha effectively takes the struct's lifetime as a parameter, despite not actually using it. This is a limitation of the syntax for defining methods on structs with lifetimes. So even though it is possible to take a pointer to it as a for<'z> fn(&Alpha<'z>) -> u8, it is not possible to treat it as a fn(&Alpha) -> u8, even though the definition suggests this should be possible.
This can be worked around by defining a function that invokes the method and take a pointer to it instead:
fn workaround(a: &Alpha) -> u8 { Alpha::alpha(a) }
Try1(workaround);
In fact, it may be better to do it the other way around, with the definition in the function and the method invoking the function. Then when the function is invoked through a fn(&Alpha) -> u8 pointer a second jump won't be necessary into the method, and calls to the method can be inlined as calls to the function.