How do I use a trait object in a BinaryHeap? [duplicate] - oop

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?

Related

mock trait implementation for concrete struct

So I have a function. And I want to test it. It takes a struct as param. And the struct has some trait implemented. This trait has a long running io method. I don't want this io method to actually go fetch data, I want to mock this io method and just return the result. I am a little lost about how this can be done. Here is my try (not working)
struct TestStruct {
a: u32,
b: u32,
}
pub trait TestTrait {
fn some_long_running_io_method(&self) -> u32 {
156
}
}
fn important_func(a: TestStruct) {
println!("a: {}", a.some_long_running_io_method());
}
impl TestTrait for TestStruct {
fn some_long_running_io_method(&self) -> u32 {
self.a + self.b
}
}
#[cfg(test)]
mod tests {
use super::*;
use mockall::predicate::*;
use mockall::*;
#[cfg(test)]
mock! {
pub TestStruct {}
impl TestTrait for TestStruct {
fn some_long_running_io_method(&self) -> u32;
}
}
#[test]
fn test_important_func() {
let mut mock = MockTestStruct::new();
mock.expect_some_long_running_io_method()
.returning(|| 1);
important_func(mock);
}
}
I obviously get this error:
error[E0308]: mismatched types
--> src/test.rs:35:24
|
35 | important_func(mock);
| -------------- ^^^^ expected struct `TestStruct`, found struct `MockTestStruct`
| |
| arguments to this function are incorrect
How can I achieve mocking trait methods? 1) One way is to change function param and instead of accepting a concrete struct accept trait. And implement this trait on MockTestStruct. But then we have dynamic dispatching and it hurts the performance. I don't want a performance degrade just for the test. 2) I also tried reimplementing the Trait right where the test is, but conflicting implementations are not allowed in Rust. 3) Make function accept TestStruct or MockTestStruct? Probably not great way either.
Could you please tell me what is the idiomatic way to do it?
You can make your function, important_func a generic function. You can then use generic bounds to restrict the type to implementors of your trait.
Here is an example with your code:
struct TestStruct {
a: u32,
b: u32,
}
pub trait TestTrait {
fn some_long_running_io_method(&self) -> u32 {
156
}
}
// important_func can now use any type T which implements TestTrait,
// including your mock implementation!
fn important_func<T: TestTrait>(a: T) {
println!("a: {}", a.some_long_running_io_method());
}
impl TestTrait for TestStruct {
fn some_long_running_io_method(&self) -> u32 {
self.a + self.b
}
}
#[cfg(test)]
mod tests {
use super::*;
use mockall::predicate::*;
use mockall::*;
#[cfg(test)]
mock! {
pub TestStruct {}
impl TestTrait for TestStruct {
fn some_long_running_io_method(&self) -> u32;
}
}
#[test]
fn test_important_func() {
let mut mock = MockTestStruct::new();
mock.expect_some_long_running_io_method().returning(|| 1);
important_func(mock);
}
}

Is there a way to split Trait implementation and defenition across different modules?

I'd like to define a trait with many methods:
pub trait DataSetT{
fn numeric_op_1(&self){...};
fn numeric_op_2(&self)->f64{...};
...
fn io_op_1(&self) {..};
fn io_op_2(&self) -> DataFrame {...};
...
}
Now, if I define all these methods in the same file it would get humongous.
For the sake of clean and visibile code, I'd like to split these definitions across different files/modules.
For example numeric operatins would live in:
src/numerics.rs
And io operations would live in
src/io.rs
Same thing with implementing this trait for a Struct (overriding the default trait behaviour).
As soon as I try doing that, I either get not all trait items implemented or confilicting definitions.
What is the best practice solution in this kind of situtation?
Without macro you should not be able to split a trait definition over different modules. Where you write trait MyTrait { .. } you need to define it.
But you can define multiple traits and have a super trait, like this:
// src/ops/a.rs
pub trait OpA {
fn op_a1(&self);
fn op_a2(&self) -> f64;
}
// src/ops/b.rs
pub trait OpB {
fn op_b1(&self);
fn op_b2(&self);
}
// src/ops/mod.rs
pub trait Op: OpA + OpB {}
// src/ops_impl/mod.rs
struct MyOp {}
impl Op for MyOp {}
// src/ops_impl/a.rs
impl OpA for MyOp {
fn op_a1(&self) {}
fn op_a2(&self) -> f64 {
42.0
}
}
// src/ops_impl/b.rs
impl OpB for MyOp {
fn op_b1(&self) {}
fn op_b2(&self) {}
}

Multiple inheritance for OOP-like Rust

I want to implement an OOP approach in Rust. My base class BaseClass would look like this (but with more parameters):
struct BaseClass {
name: String,
}
impl BaseClass {
fn new(name: &str) -> Self {
Self{name: name.to_string()}
}
}
This base class has an associated trait that does the trick for inheritance:
trait BaseClassInterface {
fn as_base(&self) -> &BaseClass;
fn get_name(&self) -> &str {
&self.as_base().name
}
}
Now, I want to inherit from this base class and add extra stuff with generics:
struct MiddleClass<T> {
base: BaseClass,
value: T,
}
impl<T> MiddleClass<T> {
fn new(name: &str, value: T) -> Self {
Self{base: BaseClass::new(name), value}
}
}
Again, this is an "abstract" class. Users of my library will "inherit" from this middle class to define their structs. So let's do a trait for it:
trait MiddleClassInterface {
type Type;
fn as_middle(&self) -> &MiddleClass<Self::Type>;
fn get_value(&self) -> &Self::Type {
&self.as_middle().value
}
}
Now we implement the BaseClassInterface trait for the MiddleClassInterface trait and we achieved "class inheritance"!
impl<T> BaseClassInterface for dyn MiddleClassInterface<Type = T> {
fn as_base(&self) -> &BaseClass {
&self.as_middle().base
}
}
So now, users can implement their own versions of the MiddleClassInterface and use methods from the BaseClassInterface trait:
struct MyIntClass {
middle: MiddleClass<i32>,
}
impl MyIntClass {
fn new(name: &str, value: i32) -> Self {
Self{middle: MiddleClass::new(name, value)}
}
}
impl MiddleClassInterface for MyIntClass {
type Type = i32;
fn as_middle(&self) -> &MiddleClass<Self::Type> {
&self.middle
}
}
As MyIntClass implements the MiddleClassInterface, MyIntClass will implement the BaseClassInterface... or not?
Let's look at my main function:
fn main() {
let my_class = MyIntClass::new("my_class", 1);
println!("{}", my_class.get_name());
}
When compiling, I get the following error:
error[E0599]: no method named `get_name` found for struct `MyIntClass` in the current scope
--> src/main.rs:71:29
|
52 | struct MyIntClass {
| ----------------- method `get_name` not found for this
...
71 | println!("{}", my_class.get_name());
| ^^^^^^^^ method not found in `MyIntClass`
|
= help: items from traits can only be used if the trait is implemented and in scope
note: `BaseClassInterface` defines an item `get_name`, perhaps you need to implement it
--> src/main.rs:13:1
|
13 | trait BaseClassInterface {
| ^^^^^^^^^^^^^^^^^^^^^^^^
For more information about this error, try `rustc --explain E0599`.
MyIntClass implements the MiddleClassInterface<i32> class, which in turn implements the BaseClassInterface trait. So... why does MyIntClass not implement the BaseClassInterface indirectly?
Thanks in advance!
Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=3aa8007b440b965aedf124a69dacc6f7
Your code will work if you change:
impl<T> BaseClassInterface for dyn MiddleClassInterface<Type = T> {
to:
impl<T> BaseClassInterface for T where T: MiddleClassInterface {
What you did above is implement BaseClassInterface for a trait object but such implementations are not considered for method lookup on concrete types. You would first have to coerce my_class into a trait for it to work:
(&my_class as &dyn MiddleClassInterface<Type=i32>).get_name())
Whereas the fix is to not implement BaseClassInterface for a trait object, but rather implement it for all types that implement the MiddleClassInterface. The difference being that MyIntClass will itself implement the base class and won't have to go through dynamic dispatch to call those methods.

What is the most idiomatic way to merge two error types?

I have a type Foo whose methods may "raise" errors of an associated type Foo::Err.
pub trait Foo {
type Err;
fn foo(&mut self) -> Result<(), Self::Err>;
}
I have another trait Bar with a method intended to process a Foo. Bar may issue errors of its own (specified by an associated type Bar::Err), but it may also encounter errors generated by the Foo it is processing.
I can see two ways to do this, but I don't know which one would be the most idiomatic to Rust.
The first one embeds a result in a result:
pub trait Bar1 {
type Err;
fn bar<F: Foo>(&mut self, foo: F) -> Result<Result<F, F::Err>, Self::Err>;
}
The second one merges the two error types into a dedicated enum:
pub trait Bar2 {
type Err;
fn bar<F: Foo>(&mut self, foo: F) -> Result<F, Choice<F::Err, Self::Err>>;
}
The second one looks semantically cleaner, but creates some hurdles for handling the additional enum.
playground
Typically you don't do a "merge", but instead use nested errors, like this.
enum IntError {
Overflow,
Underflow
}
enum StrError {
TooLong,
TooShort,
}
enum GenericError {
Int(IntError),
Str(StrError),
}
impl From<IntError> for GenericError {
fn from(e: IntError) -> Self {
GenericError::Int(e)
}
}
impl From<StrError> for GenericError {
fn from(e: StrError) -> Self {
GenericError::Str(e)
}
}
You should use a trait object Error, and you return the first error that you encounter:
pub trait Bar {
fn bar<F: Foo>(&mut self, foo: F) -> Result<F, Box<dyn Error>>;
}
or implement your trait like this:
impl Bar for MyType {
type Err = Box<dyn Error>;
fn bar<F: Foo>(&mut self, foo: F) -> Result<F, Self::Err>;
}
If you really want to have your two errors (but this is strange because one error suffices to make the process not ok), you can use a crate like failure to create an "error trace".
As a general advice, you should not forget to use the traits from std to add more semantic to your code.

How to allow multiple implementations of a trait on various types of IntoIterator items?

Rust doesn't seem to distinguish between different implementations of a trait only if they differ by an associated type.
How can I implement a method on all kinds of collections/iterators, but have specific implementations for each concrete type they contain?
error: conflicting implementations for trait Foo [E0119]
The code:
trait Foo { fn foo(self); }
impl<T> Foo for T
where T: IntoIterator<Item=u32>
{
fn foo(self) {
self.into_iter();
}
}
impl<T> Foo for T
where T: IntoIterator<Item=u16>
{
fn foo(self) {
self.into_iter();
}
}
fn main() {
vec![0u32].foo();
vec![0u16].foo();
}
You can not do the generic form directly, which is issue #20400. You'll have to introduce either a trait that can be used as a bound on T::Item to merge the two implementations, or wrapper types. E.g. the first one might look like:
trait FooIterItem {
// behaviours needed for Foo impl
}
impl FooIterItem for u32 { ... }
impl FooIterItem for u16 { ... }
impl<T> Foo for T
where T: IntoIterator, T::Item: FooIterItem
{
...
}