This question already has answers here:
How to get a reference to a concrete type from a trait object?
(2 answers)
Closed 4 months ago.
Let's say I want to store elements of different types in HashMap but all of them will implement some trait. And I want to be able to retrieve element casted to the right type. The choice of the key allows me to be sure about the type of the element (in the example below type id of the object is a part of the key). How to properly convert found element to T?
use std::collections::HashMap;
use std::any::TypeId;
use std::rc::Rc;
trait MyTrait {
fn new() -> Self where Self: Sized;
// Other methods
}
struct MyStruct {
items: HashMap<(i32, TypeId), Rc<dyn MyTrait>>
}
impl MyStruct {
fn get<T: MyTrait + 'static>(&mut self, key: i32) -> Rc<T> {
if let Some(item) = self.items.get(&(key, TypeId::of::<T>())) {
item.clone() as Rc<T> // !!! Compilation error !!!
} else {
let item = Rc::new(T::new());
self.items.insert((key, TypeId::of::<T>()), item.clone());
item
}
}
}
Rust is complaining because, on the current stable compiler, you simply can't cast between traits with as. You can however cast an Rc<dyn Any> to Rc<T> with downcast(), but it needs to be Rc<dyn Any>, not Rc<dyn MyTrait>, and it returns a Result because (as far as the compiler knows) it might fail. For example:
struct MyStruct {
items: HashMap<(i32, TypeId), Rc<dyn Any>>
}
impl MyStruct {
fn get<T: MyTrait + 'static>(&mut self, key: i32) -> Rc<T> {
if let Some(item) = self.items.get(&(key, TypeId::of::<T>())) {
item.clone().downcast::<T>().unwrap()
} else {
// ...
The trait_upcasting feature on the current nightly compiler allows you to store your values as Rc<dyn MyTrait> and upcast to Rc<dyn Any> with as later:
#![feature(trait_upcasting)]
struct MyStruct {
items: HashMap<(i32, TypeId), Rc<dyn MyTrait>>
}
impl MyStruct {
fn get<T: MyTrait + 'static>(&mut self, key: i32) -> Rc<T> {
if let Some(item) = self.items.get(&(key, TypeId::of::<T>())) {
(item.clone() as Rc<dyn Any>).downcast::<T>().unwrap()
} else {
// ...
Related
Looking for "blanket" implementation of the method(s) for trait.
Let's say for a trait
pub trait A {
fn do_a(&self);
}
want to have boxed method that wraps with box, without introducing any additional traits:
fn boxed(self) -> Box<Self>;
I can have another trait to achieve that (playground)
pub trait A {
fn do_a(&self);
}
pub trait Boxed {
fn boxed(self) -> Box<Self>;
}
impl<T> Boxed for T
where
T: A,
{
fn boxed(self) -> Box<Self> {
Box::new(self)
}
}
However, new trait Boxed is required for that.
You can add boxed directly to A with a default implementation so that structs won't need to implement it themselves:
trait A {
fn do_a(&self);
fn boxed (self) -> Box<Self>
where Self: Sized
{
Box::new (self)
}
}
struct Foo{}
impl A for Foo {
fn do_a (&self) {
todo!();
}
// No need to redefine `boxed` here
}
fn main() {
let foo = Foo{};
let _object: Box<dyn A> = foo.boxed();
}
Playground
I have a function which returns a boxed trait object, and another function that accepts a reference to an object implementing the same trait. I would like to pass a reference to the boxed trait object to the second function, but I am unable to figure out how to do this.
Example simplified code:
trait MyTrait {
fn foo(&self);
}
struct A {}
impl MyTrait for A {
fn foo(&self) {
println!("A");
}
}
struct B {}
impl MyTrait for B{
fn foo(&self) {
println!("B");
}
}
enum MyEnum {
A,
B,
}
fn create_object(my_enum: MyEnum) -> Box<dyn MyTrait> {
let boxed_value: Box<dyn MyTrait> = match my_enum {
MyEnum::A => Box::new(A{}),
MyEnum::B => Box::new(B{}),
};
boxed_value
}
fn do_something<T: MyTrait>(obj: &T) {
obj.foo();
}
fn main() {
use std::borrow::BorrowMut;
let boxed_value = create_object(MyEnum::A);
do_something(boxed_value.borrow_mut());
}
The error I get:
error[E0282]: type annotations needed
--> src\main.rs:42:5
|
42 | do_something(boxed_value.borrow_mut());
| ^^^^^^^^^^^^ ------------------------ this method call resolves to `&mut Borrowed`
| |
| cannot infer type for type parameter `T` declared on the function `do_something`
Intuitively, I would have hoped that in this case Rust would use dynamic dispatch and wouldn't care about the concrete type T (similarly to what happens in C++ when you pass a reference to a base class), but this seems not to be the case.
How do I pass a reference to the boxed trait object (Box<dyn MyTrait>) to the second function (do_something)? Is this possible in some way? A solution requiring a change to do_something would also be acceptable.
Intuitively, I would have hoped that in this case Rust would use dynamic dispatch and wouldn't care about the concrete type T (similarly to what happens in C++ when you pass a reference to a base class), but this seems not to be the case.
You can make that happen with a cast (or just type ascription, eventually) and by relaxing the default requirement for T to be Sized:
fn do_something<T: MyTrait + ?Sized>(obj: &T) {
obj.foo();
}
use std::borrow::Borrow;
let boxed_value = create_object(MyEnum::A);
do_something(boxed_value.borrow() as &dyn MyTrait);
But if you’re not otherwise using T, you can opt into dynamic dispatch on the function side much more simply:
fn do_something(obj: &dyn Borrow) {
obj.foo();
}
use std::borrow::Borrow;
let boxed_value = create_object(MyEnum::A);
do_something(boxed_value.borrow());
And if you don’t care that obj is a borrow and want to leave the option of static dispatch open, you can implement MyTrait for &dyn MyTrait:
impl MyTrait for &dyn MyTrait {
fn foo(&self) {
(*self).foo();
}
}
fn do_something<T: MyTrait>(obj: T) {
obj.foo();
}
// or, again, if not otherwise using T:
fn do_something(obj: impl MyTrait) {
obj.foo();
}
use std::borrow::Borrow;
let boxed_value = create_object(MyEnum::A);
do_something(boxed_value.borrow());
No matter what, you'll need to add ?Sized to the trait bound in do_something, and then I think you have one of three options:
(Least general) Use as_ref() on the Box when you call do_something.
fn do_something<T: MyTrait + ?Sized>(obj: &T) {
obj.foo();
}
fn main() {
let boxed_value = create_object(MyEnum::A);
do_something(boxed_value.as_ref());
}
(Most general) Replace the type of obj in do_something with impl AsRef<T>. This will make do_something work with anything convertible to a &T.
fn do_something<T: MyTrait + ?Sized>(obj: impl AsRef<T>) {
obj.as_ref().foo();
}
fn main() {
let boxed_value = create_object(MyEnum::A);
do_something(boxed_value);
}
(Medium general) Replace the type of obj in do_something with impl Deref<Target=T>. This will make do_something work with any smart pointer holding a T (which is a bit more restrictive than AsRef<T> — a type can implement AsRef<T> for as many values of T as it wants, but only gets to have one Deref implementation).
use std::ops::Deref;
fn do_something<T: MyTrait + ?Sized>(obj: impl Deref<Target=T>) {
obj.deref().foo();
}
fn main() {
let boxed_value = create_object(MyEnum::A);
do_something(boxed_value);
}
Instead of trying to unbox the value
you can instead implement MyTrait on Box<dyn MyTrait>
and forward to the boxed value.
impl MyTrait for Box<dyn MyTrait> {
fn foo(&self) {
self.deref().foo()
}
}
Then you don't even need to call borrow_mut.
fn main() {
use std::borrow::BorrowMut;
let boxed_value = create_object(MyEnum::A);
do_something(&boxed_value);
}
There's a working example in the playground
I'm working on a generic data structure that will hold pointers to some structs, and using a Handle that would normally be held by the struct, will call back into the structure to remove the pointer when the Handle is dropped. I'm unable to come up with a solution that will allow the call from the handle back into the node. I've tried a few different permutations of trait bound narrowing and associated type hackery with no success... I'm feeling like the issue is related to some kind of infinite associated type recursion (something like https://github.com/rust-lang/rust/issues/23122), but just not sure. The error is
error[E0599]: no method named `remove` found for mutable reference `&mut Node<X>` in the current scope
--> src/lib.rs:50:15
|
50 | m.remove(self.index); // <<----- ???
| ^^^^^^ method not found in `&mut Node<X>`
|
= note: the method `remove` exists but the following trait bounds were not satisfied:
`<X as FooTrait>::T = X`
pub struct Node<T: FooTrait> {
items: Vec<*mut T>,
}
impl <T: FooTrait<T=T>> Node<T> {
pub fn new() ->Node<T> {
Node {
items: Default::default(),
}
}
pub fn insert(&mut self, item: &mut T) {
let handle = Handle::new(self,0);
item.set_handle(handle);
self.items.push(item);
}
pub fn remove(&mut self, index: u16) {
self.items.swap_remove(index as usize);
}
}
pub struct Handle<X:FooTrait> {
node: *mut Node<X>,
index: u16,
}
impl <T:FooTrait> Handle<T> {
fn new(node: &mut Node<T>, i: u16) -> Handle<T> {
Handle {
node: node,
index: i
}
}
}
impl <X:FooTrait> Drop for Handle<X> {
fn drop(&mut self) {
unsafe {
let m = self.node.as_mut().unwrap();
m.remove(self.index); // <<----- ???
}
}
}
pub trait FooTrait {
type T : FooTrait;
fn set_handle(&mut self, h: Handle<Self::T>);
}
Yes this is caused by recursive constraints.
remove(&mut self, index: u16) is only defined for types that contain to T: FooTrait<T=T>, causing the FooTrait::T to reference itself, because T is that FooTrait<T=T> type.
I try to write my own RefCell-like mutable memory location but without runtime borrow checking (no overhead). I adopted the code architecture from RefCell (and Ref, and RefMut). I can call .borrow() without problems but if I call .borrow_mut() then the rust compiler says cannot borrow as mutable. I don't see the problem, my .borrow_mut() impl looks fine?
code that fails:
let real_refcell= Rc::from(RefCell::from(MyStruct::new()));
let nooverhead_refcell = Rc::from(NORefCell::from(MyStruct::new()));
// works
let refmut_refcell = real_refcell.borrow_mut();
// cannot borrow as mutable
let refmut_norefcell = nooverhead_refcell.borrow_mut();
norc.rs (No Overhead RefCell)
use crate::norc_ref::{NORefMut, NORef};
use std::cell::UnsafeCell;
use std::borrow::Borrow;
#[derive(Debug)]
pub struct NORefCell<T: ?Sized> {
value: UnsafeCell<T>
}
impl<T> NORefCell<T> {
pub fn from(t: T) -> NORefCell<T> {
NORefCell {
value: UnsafeCell::from(t)
}
}
pub fn borrow(&self) -> NORef<'_, T> {
NORef {
value: unsafe { &*self.value.get() }
}
}
pub fn borrow_mut(&mut self) -> NORefMut<'_, T> {
NORefMut {
value: unsafe { &mut *self.value.get() }
}
}
}
norc_ref.rs (data structure returned by NORefCell.borrow[_mut]()
use std::ops::{Deref, DerefMut};
#[derive(Debug)]
pub struct NORef<'b, T: ?Sized + 'b> {
pub value: &'b T,
}
impl<T: ?Sized> Deref for NORef<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
self.value
}
}
/// No Overhead Ref Cell: Mutable Reference
#[derive(Debug)]
pub struct NORefMut<'b, T: ?Sized + 'b> {
pub value: &'b mut T,
}
impl<T: ?Sized> Deref for NORefMut<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
self.value
}
}
impl<T: ?Sized> DerefMut for NORefMut<'_, T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
self.value
}
}
NORefCell::borrow_mut() takes &mut self, which requires a DerefMut on the Rc in which it is wrapped. This won't work because Rc does not give mutable references just by asking nicely (you need it to check if the reference count is exactly one, otherwise there would be multiple mutable borrows).
borrow_mut has to take &self instead of &mut self.
As mentioned in my comment: What you are basically doing is providing a safe-looking abstraction around an UnsafeCell. This is incredibly dangerous. Notice the docs regarding UnsafeCell:
The compiler makes optimizations based on the knowledge that &T is not mutably aliased or mutated, and that &mut T is unique. UnsafeCell is the only core language feature to work around the restriction that &T may not be mutated.
You are providing a thin wrapper around this powerful object, with no unsafe on the API-boundary. The "No-overhead-RefCell" is really a "no-trigger-guard-foot-gun". It does work, yet be warned about its dangers.
I am trying to store structs in a HashMap keyed by string so that I can later create new objects by string. Think of a REST API where clients can get the server to instantiate a specific object by supplying a name.
use std::collections::HashMap;
struct MyStruct;
impl MyStruct {
pub fn new() -> Self {
Self {}
}
}
struct MyOtherStruct;
impl MyOtherStruct {
pub fn new() -> Self {
Self {}
}
}
fn main() {
let mut h = HashMap::new();
h.insert("MyStruct", MyStruct);
h.insert("MyOtherStruct", MyOtherStruct);
// This is pseudo-code
let obj = h.get("MyStruct").unwrap()::new();
}
As I expected, this doesn't work due to syntax errors:
error: expected one of `.`, `;`, `?`, or an operator, found `::`
--> src/main.rs:25:41
|
25 | let obj = h.get("MyStruct").unwrap()::new();
| ^^ expected one of `.`, `;`, `?`, or an operator here
My second attempt was to store a reference to the new method of each struct instead of the types themselves.
use std::collections::HashMap;
struct MyStruct;
impl MyStruct {
pub fn new() -> Self {
Self {}
}
}
struct MyOtherStruct;
impl MyOtherStruct {
pub fn new() -> Self {
Self {}
}
}
fn main() {
let mut h = HashMap::new();
h.insert("MyStruct", &MyStruct::new);
h.insert("MyOtherStruct", &MyOtherStruct::new);
let obj = h.get("MyStruct").unwrap()();
}
This fails because the fn items have different types and can't be stored in the same HashMap:
error[E0308]: mismatched types
--> src/main.rs:22:31
|
22 | h.insert("MyOtherStruct", &MyOtherStruct::new);
| ^^^^^^^^^^^^^^^^^^^ expected fn item, found a different fn item
|
= note: expected type `&fn() -> MyStruct {MyStruct::new}`
found type `&fn() -> MyOtherStruct {MyOtherStruct::new}`
Since I'm pretty new to Rust, I'm out of ideas. How can I solve this problem?
This is ultimately fundamentally impossible. In Rust, local variables are stored on the stack, which means that they have to have a fixed size, known at compile time. Your construction requires the size of the value on the stack to be determined at runtime.
The closest alternative is to move to trait objects, which introduce a layer of indirection:
use std::collections::HashMap;
trait NewThing {
fn new(&self) -> Box<Thing>;
}
trait Thing {}
struct MyStruct;
impl NewThing for MyStruct {
fn new(&self) -> Box<Thing> {
Box::new(Self {})
}
}
impl Thing for MyStruct {}
struct MyOtherStruct;
impl NewThing for MyOtherStruct {
fn new(&self) -> Box<Thing> {
Box::new(Self {})
}
}
impl Thing for MyOtherStruct {}
fn main() {
let mut h: HashMap<_, Box<NewThing>> = HashMap::new();
h.insert("MyStruct", Box::new(MyStruct));
h.insert("MyOtherStruct", Box::new(MyOtherStruct));
let obj = h["MyStruct"].new();
}
You will find this pattern out in the world, such as in hyper's NewService.
what is [the value of &self of method new] when calling h["MyStruct"].new()
It's an instance of MyStruct or MyOtherStruct. The only reason that the same type can implement both traits is because there's no real unique state for the "factory" and the "instance". In more complicated implementations, these would be two different types.
Using the same type is common for such cases as sharing a reference-counted value.
See also:
Is it possible to have a constructor function in a trait?
Here is a more complex example of #Shepmaster's solution, using different types for Factories and the objects themselves:
use std::collections::HashMap;
trait NewThing {
fn new(&self) -> Box<Thing>;
}
trait Thing {
fn execute(&mut self);
}
// MyStruct
struct MyStructFactory;
impl NewThing for MyStructFactory {
fn new(&self) -> Box<Thing> {
Box::new(MyStruct {test: 12, name: "Test".into()})
}
}
struct MyStruct {
test: i32,
name: String
}
impl Thing for MyStruct {
fn execute(&mut self) {
self.test+=1;
println!("MyStruct {} {}", self.test, self.name);
}
}
// MyOtherStruct
struct MyOtherStructFactory;
impl NewThing for MyOtherStructFactory {
fn new(&self) -> Box<Thing> {
Box::new(MyOtherStruct {my_member: 1})
}
}
struct MyOtherStruct {
my_member: u32
}
impl Thing for MyOtherStruct {
fn execute(&mut self) { println!("MyOtherStruct.my_member: {}", self.my_member); }
}
fn main() {
let mut h: HashMap<_, Box<NewThing>> = HashMap::new();
h.insert("MyStruct", Box::new(MyStructFactory));
h.insert("MyOtherStruct", Box::new(MyOtherStructFactory));
h["MyStruct"].new().execute();
h["MyOtherStruct"].new().execute();
}
You could use std::any::Any to erase the type of the entry. They use Any::downcast<T> to check if the entry at the location matches your type, and get a Ok(Box<T>)