Implementing PartialEq on a boxed trait - rust

I have a Rust program which contains a number of different structs which all implement a trait called ApplyAction. Another struct, ActionList, contains a vector of boxed objects which implement ApplyAction. I would like to create some unit tests which compare ActionLists with one another.
There are a few different SO questions which deal with PartialEq on boxed traits, and I've used these to get some way towards an implementation. However, in the (simplified) code below (and on the Playground), the assertions in main() fail because the type ids of the objects passed to eq() differ. Why?
Also, this seems extremely complicated for such a simple use case -- is there an easier way to do this?
use std::any::TypeId;
use std::boxed::Box;
use std::fmt;
use std::mem::transmute;
#[derive(Debug, Eq, PartialEq)]
pub struct MyAction<T: fmt::Debug> {
label: T,
}
impl<T: fmt::Debug> MyAction<T> {
pub fn new(label: T) -> MyAction<T> {
MyAction { label: label }
}
}
pub trait ApplyAction<T: fmt::Debug + PartialEq>: fmt::Debug {
fn get_type(&self) -> TypeId;
fn is_eq(&self, other: &ApplyAction<T>) -> bool;
}
impl<T: fmt::Debug + Eq + 'static> ApplyAction<T> for MyAction<T> {
fn get_type(&self) -> TypeId {
TypeId::of::<MyAction<T>>()
}
fn is_eq(&self, other: &ApplyAction<T>) -> bool {
if other.get_type() == TypeId::of::<Self>() {
// Rust thinks that self and other are different types in the calls below.
let other_ = unsafe { *transmute::<&&ApplyAction<T>, &&Self>(&other) };
self.label == other_.label
} else {
false
}
}
}
impl<T: fmt::Debug + Eq + PartialEq + 'static> PartialEq for ApplyAction<T> {
fn eq(&self, other: &ApplyAction<T>) -> bool {
if other.get_type() == TypeId::of::<Self>() {
self.is_eq(other)
} else {
false
}
}
}
#[derive(Debug)]
pub struct ActionList<T: fmt::Debug> {
actions: Vec<Box<ApplyAction<T>>>,
}
impl<T: fmt::Debug + PartialEq> ActionList<T> {
pub fn new() -> ActionList<T> {
ActionList { actions: vec![] }
}
pub fn push<A: ApplyAction<T> + 'static>(&mut self, action: A) {
self.actions.push(Box::new(action));
}
}
impl<T: fmt::Debug + Eq + PartialEq + 'static> PartialEq for ActionList<T> {
fn eq(&self, other: &ActionList<T>) -> bool {
for (i, action) in self.actions.iter().enumerate() {
if **action != *other.actions[i] {
return false;
}
}
true
}
}
fn main() {
let mut script1: ActionList<String> = ActionList::new();
script1.push(MyAction::new("foo".to_string()));
let mut script2: ActionList<String> = ActionList::new();
script2.push(MyAction::new("foo".to_string()));
let mut script3: ActionList<String> = ActionList::new();
script3.push(MyAction::new("bar".to_string()));
assert_eq!(script1, script2);
assert_ne!(script1, script3);
}

In the impl<...> PartialEq for ApplyAction<T> you used TypeId::of::<Self>(); i.e. the type of the unsized trait object. That isn't what you wanted; but remove the if and directly call self.is_eq(other), and your code should be working.
Sadly your example requires a lot of code to implement ApplyAction<T> for MyAction<T> - and again for each other action type you might want to use.
I tried to remove that overhead, and with nightly features it is completely gone (and otherwise only a small stub remains):
Playground
// see `default impl` below
#![feature(specialization)]
// Any::<T>::downcast_ref only works for special trait objects (`Any` and
// `Any + Send`); having a trait `T` derive from `Any` doesn't allow you to
// coerce ("cast") `&T` into `&Any` (that might change in the future).
//
// Implementing a custom `downcast_ref` which takes any
// `T: Any + ?Sized + 'static` as input leads to another problem: if `T` is a
// trait that didn't inherit `Any` you still can call `downcast_ref`, but it
// won't work (it will use the `TypeId` of the trait object instead of the
// underlying (sized) type).
//
// Use `SizedAny` instead: it's only implemented for sized types by default;
// that prevents the problem above, and we can implement `downcast_ref` without
// worrying.
mod sized_any {
use std::any::TypeId;
// don't allow other implementations of `SizedAny`; `SizedAny` must only be
// implemented for sized types.
mod seal {
// it must be a `pub trait`, but not be reachable - hide it in
// private mod.
pub trait Seal {}
}
pub trait SizedAny: seal::Seal + 'static {
fn get_type_id(&self) -> TypeId {
TypeId::of::<Self>()
}
}
impl<T: 'static> seal::Seal for T {}
impl<T: 'static> SizedAny for T {}
// `SizedAny + ?Sized` means it can be a trait object, but `SizedAny` was
// implemented for the underlying sized type.
pub fn downcast_ref<From, To>(v: &From) -> Option<&To>
where
From: SizedAny + ?Sized + 'static,
To: 'static,
{
if TypeId::of::<To>() == <From as SizedAny>::get_type_id(v) {
Some(unsafe { &*(v as *const From as *const To) })
} else {
None
}
}
}
use sized_any::*;
use std::boxed::Box;
use std::fmt;
// `ApplyAction`
fn foreign_eq<T, U>(a: &T, b: &U) -> bool
where
T: PartialEq + 'static,
U: SizedAny + ?Sized + 'static,
{
if let Some(b) = downcast_ref::<U, T>(b) {
a == b
} else {
false
}
}
pub trait ApplyAction<T: 'static>: fmt::Debug + SizedAny + 'static {
fn foreign_eq(&self, other: &ApplyAction<T>) -> bool;
}
// requires `#![feature(specialization)]` and a nightly compiler.
// could also copy the default implementation manually to each `impl` instead.
//
// this implementation only works with sized `A` types; we cannot make
// `ApplyAction<T>` inherit `Sized`, as that would destroy object safety.
default impl<T: 'static, A: PartialEq + 'static> ApplyAction<T> for A {
fn foreign_eq(&self, other: &ApplyAction<T>) -> bool {
foreign_eq(self, other)
}
}
impl<T: 'static> PartialEq for ApplyAction<T> {
fn eq(&self, other: &ApplyAction<T>) -> bool {
self.foreign_eq(other)
}
}
// `MyAction`
#[derive(Debug, Eq, PartialEq)]
pub struct MyAction<T: fmt::Debug> {
label: T,
}
impl<T: fmt::Debug> MyAction<T> {
pub fn new(label: T) -> MyAction<T> {
MyAction { label: label }
}
}
impl<T: fmt::Debug + PartialEq + 'static> ApplyAction<T> for MyAction<T> {}
// `ActionList`
#[derive(Debug)]
pub struct ActionList<T> {
actions: Vec<Box<ApplyAction<T>>>,
}
impl<T: 'static> ActionList<T> {
pub fn new() -> ActionList<T> {
ActionList { actions: vec![] }
}
pub fn push<A: ApplyAction<T> + 'static>(&mut self, action: A) {
self.actions.push(Box::<A>::new(action));
}
}
impl<T: 'static> PartialEq for ActionList<T> {
fn eq(&self, other: &ActionList<T>) -> bool {
if self.actions.len() != other.actions.len() {
return false;
}
for (i, action) in self.actions.iter().enumerate() {
if **action != *other.actions[i] {
return false;
}
}
true
}
}
// `main`
fn main() {
let mut script1: ActionList<String> = ActionList::new();
script1.push(MyAction::new("foo".to_string()));
let mut script2: ActionList<String> = ActionList::new();
script2.push(MyAction::new("foo".to_string()));
let mut script3: ActionList<String> = ActionList::new();
script3.push(MyAction::new("bar".to_string()));
assert_eq!(script1, script2);
assert_ne!(script1, script3);
}
See also:
Object Safety

Related

How to make type-erased version of a trait with associated type?

Say there is a collection trait that has an associated type for its items:
trait CollectionItem {
// ...
}
trait Collection {
type Item: CollectionItem;
fn get(&self, index: usize) -> Self::Item;
// ...
}
Can I somehow type-erase this into a type that uses dynamic dispatch for both the Collection and the CollectionItem trait? i.e. wrap it into something like the following:
struct DynCollection(Box<dyn Collection<Item=Box<dyn CollectionItem>>>);
impl DynCollection {
fn get(&self, index: usize) -> Box<dyn CollectionItem> {
// ... what to do here?
}
}
impl <C: Collection> From<C> for DynCollection {
fn from(c: C) -> Self {
// ... what to do here?
}
}
Playground
You can add a private, type-erased helper trait:
trait DynCollectionCore {
fn get_dyn(&self, index: usize) -> Box<dyn CollectionItem>;
}
impl<C> DynCollectionCore for C
where
C: ?Sized + Collection,
C::Item: 'static,
{
fn get_dyn(&self, index: usize) -> Box<dyn CollectionItem> {
Box::new(self.get(index))
}
}
Then use this to build a wrapper type:
struct DynCollection(Box<dyn DynCollectionCore>);
impl DynCollection {
fn new<C>(inner: C) -> Self
where
C: Collection + 'static,
C::Item: 'static,
{
Self(Box::new(inner))
}
}
impl Collection for DynCollection {
type Item = Box<dyn CollectionItem>;
fn get(&self, index: usize) -> Box<dyn CollectionItem> {
self.0.get_dyn(index)
}
}
// note: something like this is also needed for `Box<dyn CollectionItem>:
// CollectionItem` to be satisfied
impl<T: ?Sized + CollectionItem> CollectionItem for Box<T> {
// ...
}

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![];

Lifetimes and ownership with traits

I am building a Rocket app and want it to manage some objects for me. For that, they need to be Send and Sync. Here's a minimal contrived example that shows the error I am getting (Playground):
trait MyTrait {
fn foo(&self) {}
}
struct TraitThing {}
impl MyTrait for TraitThing {
fn foo(&self) {
println!("Foo!");
}
}
struct Thing {
tt: &'static (dyn MyTrait + Send + Sync),
}
impl Thing {
fn with_trait(tt: &'static (dyn MyTrait + Send + Sync)) -> Self {
Self { tt }
}
}
fn main() {
let tt = TraitThing {};
let thing = Thing::with_trait(&tt);
thing.tt.foo();
}
I probably don't understand lifetimes of 'static. Ideally, I want Thing to own tt, but as far as I understand, since my TraitThing isn't Sized (and probably won't ever be), it cannot be a trait object, so it must be passed by reference.
So, how do I solve this while keeping the Send and Sync traits?
Thanks!
Additional note: I've been reading a lot about lifetimes and traits in the Rust book and other places, but I'd appreciate further reading material.
You need to use Box:
trait MyTrait {
fn foo(&self) {}
}
struct TraitThing {}
impl MyTrait for TraitThing {
fn foo(&self) {
println!("Foo!");
}
}
struct Thing {
tt: Box<dyn MyTrait + Send + Sync>,
}
impl Thing {
fn with_trait(tt: Box<dyn MyTrait + Send + Sync>) -> Self {
Self { tt }
}
}
fn main() {
let tt = TraitThing {};
let thing = Thing::with_trait(Box::new(tt));
thing.tt.foo();
}
Playground
You can use a Box but it will allocate to the Heap and use dynamic dispatch which is not useful in your case. It is useful when you want a list of different type that implements a similar Trait like :
struct Thing {
tt: Vec<Box<dyn MyTrait + Send + Sync>>,
}
But in your case you only have one element so you can use Generics in this setup:
trait MyTrait {
fn foo(&self) {}
}
struct TraitThing {}
impl MyTrait for TraitThing {
fn foo(&self) {
println!("Foo!");
}
}
struct Thing<T>
where
T: MyTrait + Send + Sync
{
tt: T,
}
impl<T> Thing<T>
where
T: MyTrait + Send + Sync
{
fn with_trait(tt: T) -> Self {
Self { tt }
}
}
fn main() {
let tt = TraitThing {};
let thing = Thing::with_trait(tt);
thing.tt.foo();
}

Cannot call a fn<T: X> from an impl<T: X> because X is not satisfied for T

I have a trait RawFd that is implemented for each type that satisfies special requirements.
Now I have a function that accepts any type that implements RawFd. And I have a trait with an implementation for each T that implements RawFd.
However I cannot call my function from my trait even if T implements RawFd:
pub trait RawFd {
fn raw_fd(&self) -> u64;
}
#[cfg(unix)]
impl<T: std::os::unix::io::AsRawFd> RawFd for T {
fn raw_fd(&self) -> u64 { self.as_raw_fd() as u64 }
}
#[cfg(windows)]
impl<T: std::os::windows::io::AsRawSocket> RawFd for T {
fn raw_fd(&self) -> u64 { self.as_raw_socket() as u64 }
}
fn print_fd<T: RawFd>(p: T) {
println!("{}", p.raw_fd());
}
trait Printable {
fn print(&self);
}
impl<T: RawFd> Printable for T {
fn print(&self) {
print_fd(self);
}
}
What is my mistake here and how can I solve it.
(Link to playground)
If you expect to use print_fd() with a reference you must do this:
fn print_fd(p: &impl RawFd) {
println!("{}", p.raw_fd());
}
or
fn print_fd<T: RawFd>(p: &T) {
println!("{}", p.raw_fd());
}
Or you must take self by value:
impl<T: RawFd> Printable for T {
fn print(self) {
print_fd(self);
}
}
Or you could use clone:
impl<T> Printable for T where T: Clone + RawFd {
fn print(&self) {
print_fd(self.clone());
}
}

Is it possible to have a struct generic over U and V, where U : V?

...and V is a trait.
Ie. Something like this, but that works?
use std::marker::PhantomData;
pub struct Foo<U, V: ?Sized> where U : V {
instance:Option<U>,
_v: PhantomData<*const V>,
}
impl<U, V> Foo<U, V> {
/// Create a new instance
pub fn new() -> Foo<U, V> {
return Foo {
instance: None,
_v: PhantomData,
};
}
/// Return trait
pub fn as_ref(&self) -> Option<&V> {
return match(self.instance) {
Some(v) => Some(&v as &V),
None => None
};
}
}
#[cfg(test)]
mod test {
use super::Foo;
trait Fooish {
fn x(&self) -> i32;
}
struct Bar;
impl Fooish for Bar {
fn x(&self) -> i32 {
return 0i32;
}
}
#[test]
fn test_new_foo() {
let _ = Foo::<Bar, Fooish>::new();
}
}
playpen link: http://is.gd/N7tWwH
No, unfortunately it is not possible (yet, maybe in the future...).
The problem is that the syntax U: V is only allowed if V is a trait and there is no way to declare that a generic parameter is a trait; ?Sized only indicate a type that may not be sized, which allows traits but also other things...
When I experimented with polymorphism, the work-around I use was to declare a trait DerivedFrom, and then I would check U: DerivedFrom<V>. Of course, it requires to implement DerivedFrom<Trait> for Struct... which is not exactly ergonomic...
You can check it here:
// Scaffolding
pub trait DerivedFrom<T: ?Sized> {}
//
trait SomeTrait {}
struct HelloWorld;
impl SomeTrait for HelloWorld {}
impl DerivedFrom<SomeTrait> for HelloWorld {}
pub struct Foo<U, V: ?Sized> where U: DerivedFrom<V> {
instance: Option<U>,
_v: std::marker::PhantomData<*const V>,
}
type HelloFoo = Foo<HelloWorld, SomeTrait>;
fn main() {
}
Note: and of course, for this very reason, we cannot have a blanket impl for DerivedFrom.

Resources