Rust - Casting super trait to trait - rust

I have a container where I can store items implementing the CommonBase trait and I have some other traits (TypeA and TypeB for example, but there can be unknown number of other TypeX traits) with the CommonBase as their super trait. I store objects implementing ItemA to container and I can get a reference to item back from the container, but as reference to the CommonBase.
I want to cast the item back to its original type like this:
fn cast_to<T: SuperTrait>(val: dyn CommonBase) -> Result<T, dyn Error> {...}
Can anyone help please?
Here is my experimental code
use std::error::Error;
use std::rc::Rc;
pub trait CommonBase {
fn to_common(self: &Self);
}
pub trait TypeA: CommonBase {
fn do_a(self: &Self);
}
pub trait TypeB: CommonBase {
fn do_b(self: &Self);
}
pub struct StructA {}
impl CommonBase for StructA {
fn to_common(self: &Self) { todo!() }
}
impl TypeA for StructA {
fn do_a(self: &Self) { todo!() }
}
pub struct StructB {}
impl CommonBase for StructB {
fn to_common(self: &Self) { todo!() }
}
impl TypeB for StructB {
fn do_b(self: &Self) { todo!() }
}
pub struct Container {
items: Vec<Rc<dyn CommonBase>>,
}
impl Container {
pub fn new() -> Container {
Container { items: Vec::new() }
}
pub fn add(self: &mut Self, item: Rc<dyn CommonBase>) {
self.items.push(item);
}
pub fn get(self, idx: usize) -> Rc<dyn CommonBase> {
self.items.get(idx).unwrap().clone()
}
}
fn cast_to<T: CommonBase>(val: Rc<dyn CommonBase>) -> Result<Rc<dyn T>, Box<dyn Error>> {...} // <- some magic is done here
fn main() {
let mut container = Container::new();
let item_a = Rc::new(StructA {});
let item_b = Rc::new(StructB {});
container.add(item_a); // index 0
container.add(item_b); // index 1
let stored_a_as_common: Rc<dyn CommonBase> = container.get_common(0); // actually TypeA
let stored_a: Rc<dyn TypeA> = cast_to(stored_a_as_common).unwrap();
stored_a.do_a();
}

Here is your code with a few additions.
For this, you need std::any::Any.
Each struct subject to dynamic cast should be known as dyn Any in order to try the dynamic cast; this is the purpose of .as_any() and .as_any_mut() in the example.
Using .downcast_ref() or .downcast_mut() requires you target a type, not a trait.
I don't think you can have at the same time two Rcs pointing towards the same struct but declared with different dyn traits.
The best to do, in my opinion, is to only deal with references (wherever they come from, Rc or something else).
You expected an Error when the dynamic cast is impossible, then I wrote the function this way.
Note however that .downcast_ref() returns an Option and this is generally good enough (None means that the cast is impossible); we could simplify the code with just this line val.as_any().downcast_ref::<T>().
use std::error::Error;
use std::rc::Rc;
use std::any::Any;
pub trait CommonBase: Any {
fn to_common(self: &Self);
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
}
pub trait TypeA: CommonBase {
fn do_a(self: &Self);
}
pub trait TypeB: CommonBase {
fn do_b(self: &Self);
}
pub struct StructA {}
impl CommonBase for StructA {
fn to_common(self: &Self) {
todo!()
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl TypeA for StructA {
fn do_a(self: &Self) {
println!("doing a");
}
}
pub struct StructB {}
impl CommonBase for StructB {
fn to_common(self: &Self) {
todo!()
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl TypeB for StructB {
fn do_b(self: &Self) {
println!("doing b");
}
}
pub struct Container {
items: Vec<Rc<dyn CommonBase>>,
}
impl Container {
pub fn new() -> Container {
Container { items: Vec::new() }
}
pub fn add(
self: &mut Self,
item: Rc<dyn CommonBase>,
) {
self.items.push(item);
}
pub fn get(
&self,
idx: usize,
) -> Rc<dyn CommonBase> {
self.items.get(idx).unwrap().clone()
}
}
fn cast_to<T: CommonBase>(
val: &dyn CommonBase
) -> Result<&T, Box<dyn Error>> {
if let Some(t_ref) = val.as_any().downcast_ref::<T>() {
Ok(t_ref)
} else {
Err("bad cast")?
}
}
#[allow(dead_code)] // unused in this example
fn cast_to_mut<T: CommonBase>(
val: &mut dyn CommonBase
) -> Result<&mut T, Box<dyn Error>> {
if let Some(t_ref) = val.as_any_mut().downcast_mut::<T>() {
Ok(t_ref)
} else {
Err("bad cast")?
}
}
fn main() {
let mut container = Container::new();
let item_a = Rc::new(StructA {});
let item_b = Rc::new(StructB {});
container.add(item_a); // index 0
container.add(item_b); // index 1
let stored_a_as_common: Rc<dyn CommonBase> = container.get(0); // actually TypeA
let stored_a: &dyn TypeA =
cast_to::<StructA>(stored_a_as_common.as_ref()).unwrap();
stored_a.do_a();
let stored_b_as_common: Rc<dyn CommonBase> = container.get(1); // actually TypeB
let stored_b: &dyn TypeB =
cast_to::<StructB>(stored_b_as_common.as_ref()).unwrap();
stored_b.do_b();
}
/*
doing a
doing b
*/

Related

Rust mutably borrowed lifetime issue

This is en excerpt from my actual code, but the issue is the same. I cannot borrow the staging_buffer in Buffer::from_data as mutable, and I cannot figure out why this is the case. Why isn't the mutable borrow of the Memory<'a> from the staging_Buffer released when the block is finished?
Note that the u32 reference in the Device struct is just a dummy, and in practice is a non-copyable type.
pub struct Device<'a> {
id: &'a u32,
}
impl<'a> Device<'a> {
pub fn new(id: &'a u32) -> Self {
Self { id }
}
}
pub struct Memory<'a> {
device: &'a Device<'a>,
}
impl<'a> Memory<'a> {
pub fn new(device: &'a Device) -> Self {
Self { device }
}
pub fn map(&mut self) {}
pub fn unmap(&mut self) {}
pub fn copy_from_host<T>(&self, _slice: &[T]) {}
}
impl<'a> Drop for Memory<'a> {
fn drop(&mut self) {}
}
pub struct Buffer<'a> {
device: &'a Device<'a>,
memory: Memory<'a>,
}
impl<'a> Buffer<'a> {
pub fn new(device: &'a Device) -> Self {
let buffer_memory = Memory::new(device);
Self {
device,
memory: buffer_memory,
}
}
pub fn from_data<T: Copy>(device: &'a Device, _data: &[T]) -> Self {
let mut staging_buffer = Buffer::new(device);
{
let staging_buffer_memory = staging_buffer.memory_mut();
staging_buffer_memory.map();
staging_buffer_memory.unmap();
}
let buffer = Self::new(device);
staging_buffer.copy_to_buffer(&buffer);
buffer
}
pub fn memory(&self) -> &Memory {
&self.memory
}
pub fn memory_mut(&'a mut self) -> &mut Memory {
&mut self.memory
}
fn copy_to_buffer(&self, _dst_buffer: &Buffer) {
//
}
}
impl<'a> Drop for Buffer<'a> {
fn drop(&mut self) {}
}
fn main() {
let id = 5;
let device = Device::new(&id);
let _buffer = Buffer::new(&device);
}
Okay, I finally figured it out!
The error here is actually in your implementation of memory_mut, and not in the rest of the code (although I agree that the lifetimes and references are making things harder than they need to be).
One way to see that this is the issue is to just mutably borrow memory directly, like so.
...
pub fn from_data<T: Copy>(device: &'a Device, _data: &[T]) -> Self {
let mut staging_buffer = Buffer::new(device);
{
// Don't use the stagin_buffer_memory variable
// let staging_buffer_memory = staging_buffer.memory_mut();
staging_buffer.memory.map();
staging_buffer.memory.unmap();
}
let buffer = Self::new(device);
staging_buffer.copy_to_buffer(&buffer);
buffer
}
...
but instead you can fix memory_mut by rewriting it like this:
...
pub fn memory_mut(&mut self) -> &mut Memory<'a> {
&mut self.memory
}
...
In this case the implementation of from_data works as it should.
I am not very confident with this, but I think it is due to the fact that the same lifetime annotation 'a is used everywhere.
Then, when lifetimes are considered by the borrow-checker, some are supposed to be equivalent (because of the same annotation) although they are not.
A lifetime is indeed necessary for the reference id in Device.
Another is needed for the reference device in Memory but this is not necessarily the same as the previous.
Then, I propagated these distinct lifetimes all over the code.
A Rust expert could probably explain this better than me.
pub struct Device<'i> {
id: &'i u32,
}
impl<'i> Device<'i> {
pub fn new(id: &'i u32) -> Self {
Self { id }
}
}
pub struct Memory<'d, 'i> {
device: &'d Device<'i>,
}
impl<'d, 'i> Memory<'d, 'i> {
pub fn new(device: &'d Device<'i>) -> Self {
Self { device }
}
pub fn map(&mut self) {}
pub fn unmap(&mut self) {}
pub fn copy_from_host<T>(
&self,
_slice: &[T],
) {
}
}
impl<'d, 'i> Drop for Memory<'d, 'i> {
fn drop(&mut self) {}
}
pub struct Buffer<'d, 'i> {
device: &'d Device<'i>,
memory: Memory<'d, 'i>,
}
impl<'d, 'i> Buffer<'d, 'i> {
pub fn new(device: &'d Device<'i>) -> Self {
let buffer_memory = Memory::new(device);
Self {
device,
memory: buffer_memory,
}
}
pub fn from_data<T: Copy>(
device: &'d Device<'i>,
_data: &[T],
) -> Self {
let mut staging_buffer = Buffer::new(device);
{
let staging_buffer_memory = staging_buffer.memory_mut();
staging_buffer_memory.map();
staging_buffer_memory.unmap();
}
let buffer = Self::new(device);
staging_buffer.copy_to_buffer(&buffer);
buffer
}
pub fn memory(&self) -> &Memory<'d, 'i> {
&self.memory
}
pub fn memory_mut(&mut self) -> &mut Memory<'d, 'i> {
&mut self.memory
}
fn copy_to_buffer(
&self,
_dst_buffer: &Buffer,
) {
//
}
}
impl<'d, 'i> Drop for Buffer<'d, 'i> {
fn drop(&mut self) {}
}
fn main() {
let id = 5;
let device = Device::new(&id);
let _buffer = Buffer::new(&device);
}

trait for something generic, but errors could be specific for trait implementation

Let's say I have a trait for a Decoder, and I have specific decoders that implement this trait:
pub trait Decoder {
fn do_something(&self) -> Result<(), DoSomethingError>;
}
pub enum DoSomethingError {}
pub struct SpecificDecoder1 {}
impl Decoder for SpecificDecoder1 {
fn do_something(&self) -> Result<(), DoSomethingError> {
todo!()
}
}
pub struct SpecificDecoder2 {}
impl Decoder for SpecificDecoder2 {
fn do_something(&self) -> Result<(), DoSomethingError> {
todo!()
}
}
each decoder could return specific errors. However, I don't want to include these specific errors in the DoSomethingError trait as some decoders could not even be available on some devices, so I don't want the mess of #cfg(feature="...") on the enum.
I thought about
pub trait DoSomethingError {}
fn do_something(&self) -> Result<(), Box<dyn DoSomethingError>> {
Ok(self.do_another_something()?)
}
this way I can downcast the Box<dyn DoSomethingError> to a specific error if needed. However, Ok(self.do_another_something()?) wouldn't work. Remember that I don't want to store a string, I want to be able to match the error somehow, or try to match it, so I cannot create a UserDefined(String) variant on the string and convert every error to it.
If you want to have a different Error for each implementation, you can define the trait with an associated error type, just like in TryFrom:
pub trait Decoder {
type Error;
fn do_something(&self) -> Result<(), Self::Error>;
}
pub enum SpecificDecoder1Error {}
pub struct SpecificDecoder1 {}
impl Decoder for SpecificDecoder1 {
type Error = SpecificDecoder1Error;
fn do_something(&self) -> Result<(), Self::Error> {
todo!()
}
}
pub enum SpecificDecoder2Error {}
pub struct SpecificDecoder2 {}
impl Decoder for SpecificDecoder2 {
type Error = SpecificDecoder2Error;
fn do_something(&self) -> Result<(), Self::Error> {
todo!()
}
}
But if you want the trait to return a generic type, you can return a dyn std::any::Any value, and downcast if necessary:
pub trait Decoder {
fn do_something(&self) -> Result<(), Box<dyn std::any::Any>>;
}
pub struct SpecificDecoder1Error(u16);
pub struct SpecificDecoder1 {}
impl Decoder for SpecificDecoder1 {
fn do_something(&self) -> Result<(), Box<dyn std::any::Any>> {
Err(Box::new(SpecificDecoder1Error(13)))
}
}
pub struct SpecificDecoder2Error(String);
pub struct SpecificDecoder2 {}
impl Decoder for SpecificDecoder2 {
fn do_something(&self) -> Result<(), Box<dyn std::any::Any>> {
Err(Box::new(SpecificDecoder2Error(
"Specific Error 2".to_string(),
)))
}
}
fn main() {
let dec1 = SpecificDecoder1 {};
let dec2 = SpecificDecoder2 {};
let decs: [&dyn Decoder; 2] = [&dec1, &dec2];
for dec in decs {
let error = dec.do_something().err().unwrap();
if let Some(error1) = error.downcast_ref::<SpecificDecoder1Error>() {
println!("Error from SpecificDecoder1Error: {}", error1.0)
} else if let Some(error2) =
error.downcast_ref::<SpecificDecoder2Error>()
{
println!("Error from SpecificDecoder2Error: {}", error2.0)
} else {
unreachable!()
}
}
}

How to define a Vec in Rust to support containing multiple types of structs that implement a same trait?

In c++, I can define a parent class, and the type in vector can just be the father class type. So how to implement that in Rust?
Like for this example:
I defined two types of Integer who both implement the trait Object, now I want to put them in a same vector, is there any way to achieve that?
pub trait Object<T: Object<T>+Clone> {
fn sub(&self, x: &T) -> T {
x.clone() //this is a useless implementation, just for structs don't need to implement this method.
}
}
#[derive(Debug, Copy, Clone)]
pub struct Integer {
val: i32
}
impl Integer {
pub fn get(&self) -> i32 {
self.val
}
pub fn new(val: i32) -> Self {
Integer {
val
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct Int {
val: i32
}
impl Int {
pub fn get(&self) -> i32 {
self.val
}
pub fn new(val: i32) -> Self {
Int {
val
}
}
}
impl Object<Int> for Int {
fn sub(&self, rhs: &Int) -> Int {
Int {
val: self.val - rhs.get()
}
}
}
impl Object<Integer> for Integer {
fn sub(&self, rhs: &Integer) -> Integer {
Integer {
val: self.val - rhs.get()
}
}
}
fn main() {
let mut v: Vec<Box<dyn Object>> = Vec::new();
v.push(Box::new(Integer::new(1)));
v.push(Box::new(Int::new(2)));
}
Thanks a lot.
There are several aspects of your design that don't fit in Rust:
trait Object<T: Object<T>+Clone> doesn't help - Rust doesn't do CRTP, just use Self instead.
for Object to be object-safe (necessary to put it in a vector), it can't be parameterized by type. A type parameter means you get a completely separate trait for each type.
Object::sub() can't return the result by value, because the size of the value can differ for different implementations, so it wouldn't be object-safe. It must return Box<dyn Object> instead.
The code modified as indicated looks like this:
pub trait Object {
fn get(&self) -> i32;
fn sub(&self, x: &dyn Object) -> Box<dyn Object>;
}
#[derive(Debug, Copy, Clone)]
pub struct Integer {
val: i32,
}
impl Integer {
fn new(val: i32) -> Box<dyn Object> {
Box::new(Int { val })
}
}
impl Object for Integer {
fn get(&self) -> i32 {
self.val
}
fn sub(&self, rhs: &dyn Object) -> Box<dyn Object> {
Integer::new(self.val - rhs.get())
}
}
#[derive(Debug, Copy, Clone)]
pub struct Int {
val: i32,
}
impl Int {
fn new(val: i32) -> Box<dyn Object> {
Box::new(Int { val })
}
}
impl Object for Int {
fn get(&self) -> i32 {
self.val
}
fn sub(&self, rhs: &dyn Object) -> Box<dyn Object> {
Int::new(self.val - rhs.get())
}
}
fn main() {
let mut v: Vec<Box<dyn Object>> = vec![];
v.push(Integer::new(1));
v.push(Int::new(2));
v.push(v[0].sub(v[1].as_ref()));
for o in &v {
println!("{}", o.get());
}
}
Playground
I think you can combine trait and provide a blank implementation, then use that in vector
trait TraitA {}
trait TraitB {}
trait CombinedTraitATraitB: TraitA + TraitB {}
impl<T> CombinedTraitATraitB for T where T: TraitA + TraitB {}
let vector: Vec<Box<dyn CombinedTraitATraitB>> = vec![];

Converting an Option<impl Trait> to an Option<Box<dyn Trait>>

Is it possible to map an Option<impl Trait> to a Option<Box<dyn Trait>>? If the argument to new() is not an Option, it is possible to assign it to a struct with Some(Box::new(item)). Why does this work and the map doesn't?
trait TestTrait {}
impl TestTrait for i32 {}
struct TestStruct {
item: Option<Box<dyn TestTrait>>
}
impl TestStruct {
pub fn new(item: Option<impl TestTrait + 'static>) -> Self {
let item: Option<Box<dyn TestTrait>> = item.map(|i| Box::new(i));
Self {
item
}
}
}
fn main() {
let num:i32 = 0;
let s = TestStruct::new(Some(num));
}
It looks like the compiler can not inference the correct type for the closure. If you specify it explicitly then everything works (playground):
trait TestTrait {}
impl TestTrait for i32 {}
struct TestStruct {
item: Option<Box<dyn TestTrait>>
}
impl TestStruct {
pub fn new(item: Option<impl TestTrait + 'static>) -> Self {
let item = item.map(
|i| -> Box<dyn TestTrait> {
Box::new(i)
}
);
Self {
item
}
}
}
fn main() {
let num:i32 = 0;
let _ = TestStruct::new(Some(num));
}

thread 'main' has overflowed its stack in Rust

I'm trying to learn Rust (I come from Java) and I'm having some problems.
I'm building a simple program that is the base for a connection pool.
When I run it, I get the runtime error thread 'main' has overflowed its stack and I cannot understand why.
here is main.rs
use hello_rust::concurrent_bag;
use hello_rust::concurrent_bag::BagEntry;
fn main() {
let my_bagentry = BagEntry::new(String::from("ciao"));
//println!("{}", my_bagentry.value());
let mut contVec : Vec<BagEntry<String>>=vec![];
contVec.push(my_bagentry);
println!("state ={}", contVec[0]);
println!("state ={}", contVec[0].value());
let mut my_bag: concurrent_bag::ConcurrentBag<String> =concurrent_bag::ConcurrentBag::new();
my_bag.addEntry(String::from("ciao Entry"));
let result = my_bag.borrowEntry();
if result.is_some() {
println!("{}", result.unwrap().value());
}
}
and here is lib.rs
pub mod concurrent_bag {
use crate::concurrent_bag::BagEntryState::UNUSED;
pub enum BagEntryState {
UNUSED, USED, REMOVED
}
impl fmt::Display for BagEntryState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
BagEntryState::UNUSED => write!(f, "UNUSED"),
BagEntryState::USED => write!(f, "USED"),
BagEntryState::REMOVED => write!(f, "REMOVED"),
}
}
}
impl PartialEq for BagEntryState {
fn eq(&self, other: &Self) -> bool {
self == other
}
}
pub struct BagEntry< T: std::cmp::PartialEq + fmt::Display> {
state : BagEntryState,
value: T,
}
impl<'a, T: std::cmp::PartialEq + fmt::Display> BagEntry<T> {
pub fn new(value: T) -> BagEntry< T> {
BagEntry {
value,
state: UNUSED,
}
}
pub fn value(&self)->&T {
&self.value
}
pub fn state(&self)->&BagEntryState {
&self.state
}
}
impl<'a, T: std::cmp::PartialEq + fmt::Display> PartialEq for BagEntry<T> {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl<T: std::cmp::PartialEq + fmt::Display> fmt::Display for BagEntry<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
use std::sync::Arc;
use core::fmt;
pub struct ConcurrentBag<T: std::cmp::PartialEq + fmt::Display> {
entry_list:Vec<BagEntry<T>>,
}
impl<'a, T: std::cmp::PartialEq + fmt::Display> ConcurrentBag<T> {
pub fn new() -> ConcurrentBag<T> {
ConcurrentBag {
entry_list: vec![],
}
}
pub fn borrowEntry(&self) -> Option<BagEntry<T>> {
println!("borrow vc size {}", self.entry_list.len());
println!("value ={}", (self).entry_list[0].value());
println!("state ={}", (self).entry_list[0].state());
if (self).entry_list[0].state()==&UNUSED {
}
let result:Option<BagEntry<T>> =None;
result
}
pub fn addEntry(&mut self, value: T) {
let my_bagentry = BagEntry::new(value);
(*self).entry_list.push(my_bagentry);
println!("addEntry vc size {}", self.entry_list.len())
}
pub fn removeEntry(&mut self, value: T) {
let my_bagentry = BagEntry::new(value);
let index =(*self).entry_list.iter().position(|x| *x == my_bagentry).unwrap();
self.entry_list.remove(index);
}
}
}
The problematic line is
if (self).entry_list[0].state()==&UNUSED
and I can't undestrand why since the line
println!("state ={}", (self).entry_list[0].state());
seems to work well.
Another issue that puzzles me is that if I use &self in borrowEntry I should use *self for getting access to entry_list but the program compiles and runs without errors.
Doesn't this code repeat itself forever?
impl PartialEq for BagEntryState {
fn eq(&self, other: &Self) -> bool {
self == other
}
}
We implement PartialEq to make it possible to compare instances of a type with == and != operators. So, it doesn't make sense to use self == other in there.
You can just derive PartialEq.
#[derive(PartialEq)]
pub enum BagEntryState {
UNUSED,
USED,
REMOVED,
}
Or, if you want to implement it by hand, you can do something like this.
impl PartialEq for BagEntryState {
fn eq(&self, other: &BagEntryState) -> bool {
match (self, other) {
(&BagEntryState::UNUSED, &BagEntryState::UNUSED)
| (&BagEntryState::USED, &BagEntryState::USED)
| (&BagEntryState::REMOVED, &BagEntryState::REMOVED) => true,
_ => false,
}
}
}

Resources