Specialization: PartialEq vs Rc - rust

I'd like to come up with a function that does a best-effort equality check on two values. For T: PartialEq it's easy, and for Rc<T> it's sufficient to use Rc::ptr_eq when T is not PartialEq.
I'm attempting to use specialization to provide the Rc<T: !PartialEq> case but I'm having problems with overlapping implementations, since Rc could implement PartialEq.
Is there any way to come up with a single ref_eq function, with or without specialization?
My code: (playground):
#![feature(specialization)]
use std::fmt::Display;
use std::rc::Rc;
trait RefEq {
fn ref_eq(&self, other: &Self) -> bool;
}
impl<T> RefEq for T {
default fn ref_eq(&self, _other: &Self) -> bool {
false
}
}
impl<T> RefEq for T where T: PartialEq {
default fn ref_eq(&self, other: &Self) -> bool {
self == other
}
}
// error[E0119]: conflicting implementations of trait `RefEq` for type `std::rc::Rc<_>`:
/*
impl<T> RefEq for Rc<T> {
fn ref_eq(&self, other: &Self) -> bool {
Rc::ptr_eq(self, other)
}
}
*/
fn assert_ref_eq<T>() where T: RefEq { }
fn main() {
assert_ref_eq::<f64>(); // f64 is PartialEq => ok
assert_ref_eq::<Rc<String>>(); // String is PartialEq => ok
assert_ref_eq::<Rc<dyn Display>>(); // dyn Display is not PartialEq =>
// please use Rc::ptr_eq
}

Related

Implement methods for trait without additional traits

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

Automatically implement a trait for a type that already satisfies it

TL;DR. Is it possible to automatically implement a trait for an existing type that already has all methods required by the trait?
Long version. Suppose I want to have a generic function that does stack operations on any stack-like type. So I have a trait
pub trait StackLike<T> {
fn is_empty(&self) -> bool;
fn pop(&mut self) -> Option<T>;
fn push(&mut self, value: T);
}
which I can now use like this: pub fn foo(s: &mut dyn StackLike<i32>) { s.push(42); }. So far so good.
There are several existing types that already satisfy my trait, so implementing it is trivial, e.g.
impl<T> StackLike<T> for Vec<T> {
fn is_empty(&self) -> bool { self.is_empty() }
fn pop(&mut self) -> Option<T> { self.pop() }
fn push(&mut self, value: T) { self.push(value) }
}
impl<T, const N: usize> StackLike<T> for SmallVec<[T; N]> {
fn is_empty(&self) -> bool { self.is_empty() }
fn pop(&mut self) -> Option<T> { self.pop() }
fn push(&mut self, value: T) { self.push(value) }
}
This works, but it's a lot of boilerplate. Absolutely nothing interesting happens here: method names, argument types and return type — everything is same.
Question. Is it possible to avoid spelling out all these tautological statements, i.e. do something like the snippet below?
#[implement-trait-automagically]
impl<T> StackLike<T> for Vec<T>;
#[implement-trait-automagically]
impl<T, const N: usize> StackLike<T> for SmallVec<[T; N]>;
Or may be I can mark my trait somehow so that all types that can satisfy it automatically satisfy it (similar to how concepts work in C++)?
Rust doesn't have this kind of "duck typing" but you can put the implementation behind a macro to achieve your stated goals of avoiding all of the boilerplate code while keeping the implementations identical and error-free:
pub trait StackLike<T> {
fn is_empty(&self) -> bool;
fn pop(&mut self) -> Option<T>;
fn push(&mut self, value: T);
}
macro_rules! stacklike_impl {
() => {
fn is_empty(&self) -> bool { self.is_empty() }
fn pop(&mut self) -> Option<T> { self.pop() }
fn push(&mut self, value: T) { self.push(value) }
}
}
impl<T> StackLike<T> for Vec<T> { stacklike_impl!(); }
impl<T, const N: usize> StackLike<T> for SmallVec<[T; N]> { stacklike_impl!(); }

Is it possible to implement `PartialEq` for a trait reference that returns `true` for implementations instances that have the same inner content? [duplicate]

Editor's note: This code example is from a version of Rust prior to 1.0 and is not syntactically valid Rust 1.0 code. Updated versions of this code produce different errors, but the answers still contain valuable information.
It seems like we cannot test for equality in the following case. Why is this? Is there a workaround? (I am using Rust 0.11).
trait A: PartialEq {}
#[deriving(PartialEq)]
enum T {Ta, Tb}
impl A for T {}
fn main() {
assert!(Ta == Ta);
assert!(Ta != Tb);
assert!(some_fn(&Ta, &Ta));
assert!(!some_fn(&Ta, &Tb));
}
fn some_fn(an_a: &A, another_a: &A) -> bool {
an_a == another_a
// ERROR ^~~~~~~~~~~~ binary operation `==` cannot be applied to type `&A`
}
fn another_fn(an_a: &A + PartialEq, another_a: &A + PartialEq) -> bool {
// ERROR: ^~~~~~~~~ only the builtin traits can be used as closure or object bounds
an_a == another_a
}
With help from Vladimir Matveev, I figured out how to use Any to downcast my trait to a concrete type and test the resulting value for equality:
// `Any` allows us to do dynamic typecasting.
use std::any::Any;
trait A {
// An &Any can be cast to a reference to a concrete type.
fn as_any(&self) -> &dyn Any;
// Perform the test.
fn equals_a(&self, _: &dyn A) -> bool;
}
#[derive(Debug, PartialEq)]
enum T {
Ta,
Tb,
}
// Implement A for all 'static types implementing PartialEq.
impl<S: 'static + PartialEq> A for S {
fn as_any(&self) -> &dyn Any {
self
}
fn equals_a(&self, other: &dyn A) -> bool {
// Do a type-safe casting. If the types are different,
// return false, otherwise test the values for equality.
other
.as_any()
.downcast_ref::<S>()
.map_or(false, |a| self == a)
}
}
fn main() {
assert_eq!(T::Ta, T::Ta);
assert_ne!(T::Ta, T::Tb);
assert!(some_fn(&T::Ta, &T::Ta));
assert!(!some_fn(&T::Ta, &T::Tb));
}
fn some_fn(an_a: &dyn A, another_a: &dyn A) -> bool {
// It works!
an_a.equals_a(another_a)
}
Here is the definition of the PartialEq trait:
pub trait PartialEq<Rhs = Self>
where
Rhs: ?Sized,
{
fn eq(&self, other: &Rhs) -> bool;
fn ne(&self, other: &Rhs) -> bool { ... }
}
Note the Self parameter type. This means that eq() and ne() methods accept a parameter of the same type as implementor. For example:
impl PartialEq for i32 {
fn eq(&self, other: &i32) -> bool { ... }
}
impl PartialEq for String {
fn eq(&self, other: &String) -> bool { ... }
}
Note how type of other changes to reflect the type PartialEq is implemented for.
This is the problem. In trait objects, the actual type is erased and unavailable at runtime. This means that it is impossible to obtain a reference to a concrete type from a trait object; in particular, you can't go from &A to &T in your example.
This means that it is impossible to call methods accepting or returning the Self type on trait objects. Indeed, these methods always require a concrete type, but if you have only a trait object, there is no concrete type, and there is no way such method could work in any sensible way.
In certain cases of trait objects, you wish to compare them based on some properties exposed via the trait. You can achieve this by implementing methods on the trait type itself:
trait A {
fn id(&self) -> i32;
}
impl PartialEq for dyn A + '_ {
fn eq(&self, other: &Self) -> bool {
self.id() == other.id()
}
}
impl Eq for dyn A + '_ {}
fn some_fn(an_a: &dyn A, another_a: &dyn A) -> bool {
an_a == another_a
}
This doesn't directly address the original case which wants to delegate back to the implementation of PartialEq of the underlying type, but you can combine the existing solution:
impl PartialEq for dyn A + '_ {
fn eq(&self, other: &Self) -> bool {
self.equals_a(other)
}
}
See also:
Why would I implement methods on a trait instead of as part of the trait?

Implementing PartialEq on a boxed trait

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

Enforce Eq check on trait

Suppose the following objects:
pub struct MyStruct<T>{
items: Vec<T>
}
pub impl<T> MyStruct<T> {
pub fn new() -> MyStruct {
MyStruct{ items::new(); }
}
pub fn add(&mut self, item: T) where T : Eq {
self.items.push(item);
}
}
pub trait C {}
pub struct Another {
mystruct: MyStruct<Box<C>>
}
pub impl Another {
pub fn exec<C>(&mut self, c: C) where C: Eq + C {
self.mystruct.add(c);
}
}
On exec I'm enforcing that C also is Eq, but I'm receiving the following error:
error: the trait `core::cmp::Eq` is not implemented for the type `C`
I had to make
pub impl<T> MyStruct<T>
instead of
pub impl<T : Eq> MyStruct<T>
because since C is a trait I cant enforce Eq when using MyStruct::new, so I left the check for the type guard on the function. What's happening here?
Let’s look at the relevant definitions for Eq:
pub trait Eq: PartialEq<Self> {
…
}
pub trait PartialEq<Rhs: ?Sized = Self> {
fn eq(&self, other: &Rhs) -> bool;
…
}
Now consider MyStruct<Box<C>>: the type that it wants to implement Eq is Box<C>, a boxed trait object. To implement Eq, Box<C> must first implement PartialEq<Box<C>>, like this:
impl PartialEq for Box<C> {
fn eq(&self, other: &Box<C>) -> bool;
}
impl Eq for Box<C> { }
That is, you must be able to compare an arbitrary Box<C> with any other arbitrary Box<C>. The boxed trait objects that you are comparing could be of different concrete types, here. Thus you need to write this implementation manually. In such cases, you will typically want the trait to include some way of normalising the form of the object into a concrete, comparable type; for some types this is obvious; if AsRef<T> was to have a PartialEq implementation added, the implementation to add would be fairly obvious:
impl<T: PartialEq> PartialEq for AsRef<T> {
fn eq(&self, other: &AsRef<T>) -> bool {
self.as_ref() == other.as_ref()
}
}
(Note also that if C implements PartialEq, Box<C> does, so such implementations as we’re discussing should go on the unboxed trait object, not on the boxed trait object.)
Quite often, however, there is not an obvious and simple implementation. There are a few approaches you could take:
Convert the objects (potentially expensively, though ideally cheaply) into some base type, e.g. a String which can then be compared;
Give up;
Constrain C to 'static types and use some fancy Any magic to make it so that it uses the base type’s PartialEq implementation, returning false if the two trait objects are not of the same concrete type. This one is a fairly useful one, so I’ll give some code for it:
#![feature(core)]
use std::any::{Any, TypeId};
use std::mem;
fn main() { }
trait PartialEqFromC {
fn eq_c(&self, other: &C) -> bool;
}
impl<T: PartialEq + Any + C> PartialEqFromC for T {
fn eq_c(&self, other: &C) -> bool {
if other.get_type_id() == TypeId::of::<Self>() {
self == unsafe { *mem::transmute::<&&C, &&Self>(&other) }
} else {
false
}
}
}
trait C: Any + PartialEqFromC {
}
impl PartialEq for C {
fn eq(&self, other: &C) -> bool {
self.eq_c(other)
}
}
Note that this example depends on the unstable feature core for Any.get_type_id and is thus tied to nightly only; this can be worked around by duplicating that definition from the Any trait into a new supertrait of C and could also be simplified by mopafying the C trait.

Resources