I am struggling to make this work:
trait HasVoice: PartialEq + Eq + Hash {
fn talk(self: &Self) {}
}
#[derive(PartialEq, Hash, Eq)]
struct Duck {
name: String,
}
#[derive(PartialEq, Hash, Eq)]
struct Dog {
breed: String,
}
impl HasVoice for Duck {
fn talk(self: &Self) {
println!("duck quack!")
}
}
impl HasVoice for Dog {
fn talk(self: &Self) {
println!("dog bark!")
}
}
fn test() {
let duck: Duck = Duck {
name: "duckie".to_string(),
};
let dog: Dog = Dog {
breed: "labrador".to_string(),
};
let mut quack_set: HashSet<Box<dyn HasVoice>> = HashSet::new();
quack_set.insert(duck);
quack_set.insert(dog);
}
I am getting:
the trait `HasVoice` cannot be made into an object
`HasVoice` cannot be made into an objectrustcClick for full compiler diagnostic
main.rs(59, 17): for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
TL;DR: You didn't specify how to compare Dogs and Ducks, so the compiler is complaining at you. You have to provide a way for them to be compared.
The solution to this is way more complicated than I was expecting, and probably isn't a good idea. You should just use an enum Voiced { Dog(Dog), Duck(Duck) } instead.
The problem
If you view the full compiler diagnostics, you will see the following:
error[E0038]: the trait `HasVoice` cannot be made into an object
--> src/lib.rs:39:36
|
39 | let mut quack_set: HashSet<Box<dyn HasVoice>> = HashSet::new();
| ^^^^^^^^^^^^ `HasVoice` cannot be made into an object
|
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
--> src/lib.rs:4:17
|
4 | trait HasVoice: PartialEq + Eq + Hash {
| -------- ^^^^^^^^^ ...because it uses `Self` as a type parameter
| |
| this trait cannot be made into an object...
You can see there's a problem with PartialEq. But what is it?
What's the big deal with PartialEq?
The problem is that PartialEq is declared as PartialEq<Rhs = Self>, which defaults to the trait PartialEq<Self> if you don't specify a type parameter. In other words, PartialEq by default provides the comparison of a type with itself.
Consider the following code:
let duck: &dyn HasVoice = &Duck { /* ... */ };
let dog: &dyn HasVoice = &Dog { /* ... */ };
println!("{}", dog == duck);
What do you expect the outcome to be?
Well, dog == duck is equivalent to dog.eq(duck), right? So Rust will look up the vtable for dog, see that dog does in fact have an eq method of type fn eq(&self, rhs: Dog), and... wait a second. The rhs is explicitly not a Dog, it's a &dyn HasVoice, which contains a pointer to the type Duck. So there's absolutely no way you can call eq on these two types.
This issue was discovered back in 2015, and the decided solution was to ban any Self parameters in supertraits that aren't &self. This means that PartialEq<Self> cannot be used to compare two Box<dyn HasVoice> types.
So then what?
How do I fix this?
Before we know how to fix the issue, we need to know first:
How do we compare Dog and Duck?
Do they compare unequal always? Equal always? Something more exotic? The compiler doesn't know, only you do, so you should specify it. What should the answer be?
Let's say that Dogs and Ducks are always unequal. This is probably what most people expect from dogs and ducks in real life. How do we achieve that?
Well, you can do a bunch of trait machinery manually, but the easiest way is to use the dyn_partial_eq crate.
Here's how:
use dyn_partial_eq::*;
#[dyn_partial_eq]
trait HasVoice {
fn talk(&self);
}
#[derive(DynPartialEq, PartialEq, Eq, Hash)]
struct Duck {
name: String,
}
#[derive(DynPartialEq, PartialEq, Eq, Hash)]
struct Dog {
breed: String,
}
Then PartialEq works properly, right? So all is good?
Well, no. We forgot about Hash.
How do we implement Hash?
The Hash trait is not object-safe. If you add it to the trait requirements, you'll just get a compile error. This is because Hash::hash is a generic function, so it can't be put in a vtable.
We can get around this by making a DynHash trait that isn't generic and instead takes a &mut dyn reference. Here's how you might do it:
trait DynHash {
fn dyn_hash(&self, state: &mut dyn Hasher);
}
impl<H: Hash + ?Sized> DynHash for H {
fn dyn_hash(&self, mut state: &mut dyn Hasher) {
self.hash(&mut state);
}
}
#[dyn_partial_eq]
trait HasVoice: DynHash {
fn talk(&self);
}
impl Hash for dyn HasVoice {
fn hash<H: Hasher>(&self, state: &mut H) {
self.dyn_hash(state)
}
}
impl Eq for Box<dyn HasVoice> {} // required for HashSet
That's all you need.
The final code
use std::hash::{Hash, Hasher};
use std::collections::HashSet;
use dyn_partial_eq::*;
trait DynHash {
fn dyn_hash(&self, state: &mut dyn Hasher);
}
impl<H: Hash + ?Sized> DynHash for H {
fn dyn_hash(&self, mut state: &mut dyn Hasher) {
self.hash(&mut state);
}
}
#[dyn_partial_eq]
trait HasVoice: DynHash {
fn talk(&self);
}
impl Hash for Box<dyn HasVoice> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.dyn_hash(state)
}
}
impl Eq for Box<dyn HasVoice> {}
#[derive(DynPartialEq, PartialEq, Eq, Hash, Clone)]
struct Duck {
name: String,
}
#[derive(DynPartialEq, PartialEq, Eq, Hash, Clone)]
struct Dog {
breed: String,
}
impl HasVoice for Duck {
fn talk(&self) {
println!("duck quack!")
}
}
impl HasVoice for Dog {
fn talk(&self) {
println!("dog bark!")
}
}
fn main() {
let duck: Duck = Duck {
name: "an animal".to_string(),
};
let dog: Dog = Dog {
breed: "an animal".to_string(),
};
let dog2: Dog = Dog {
breed: "an animal".to_string(),
};
let mut quack_set: HashSet<Box<dyn HasVoice>> = HashSet::new();
quack_set.insert(Box::new(duck.clone()));
quack_set.insert(Box::new(dog.clone()));
quack_set.insert(Box::new(dog2));
assert_eq!(quack_set.len(), 2);
}
So that's how you implement Java in Rust. :P
For future improvement: The problem with DynHash
If you actually test the hash function we implemented here, you will notice that it doesn't work that well. More specifically, Dog { breed: "the same string".to_string() } and Duck { name: "the same string".to_string() } hash to exactly the same value, because Hash only cares about the stored data, not the unique type. This isn't a problem in theory, but it is quite annoying, because we really should not get hash collisions between these two types that easily. There's probably a way to fix this with a proc macro, similar to dyn_partial_eq, but that's probably not worth doing here.
Related
I am trying to understand why the following code does not compile:
trait Vehicle {
fn get_num_wheels(&self) -> u32;
}
impl std::fmt::Display for dyn Vehicle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Has {} wheels", self.get_num_wheels())
}
}
struct Car();
impl Vehicle for Car {
fn get_num_wheels(&self) -> u32 {
4
}
}
fn main() {
let car = Car {};
println!("{car}");
}
error[E0277]: `Car` doesn't implement `std::fmt::Display`
--> src/main.rs:21:16
|
21 | println!("{car}");
| ^^^ `Car` cannot be formatted with the default formatter
|
= help: the trait `std::fmt::Display` is not implemented for `Car`
I would think that if I implemented Display for Vehicle, then all structs that implement Vehicle would also inherit Vehicle's implementation of Display. I can see why this would be an issue if Car tried to make its own implementation of Display, but this is not the case here.
I know I can fix this example by changing
impl std::fmt::Display for dyn Vehicle
to
impl std::fmt::Display for Car
but for non-trivial examples, this seems really verbose. What's the right way to do this?
The type dyn Vehichle is not the same as the trait Vehichle. Specifically, it's what's called a trait object, and can hold a value of any type that implements the trait.
Therefore, when you implement Display for dyn Vehichle, you only implement it for that particular type, but not for any other type that implements the trait.
If you want every type that implements a trait TraitA (e.g. Vehicle) to also implement a trait TraitB (e.g. Display), there are two ways of approaching this, each with some caveats.
The first is to have a blanket implementation. Due to the orphan rule, this can only be done if TraitB is defined in the same crate, so this wouldn't work with Display which is defined in the standard library.
impl<T: TraitA> TraitB for T {
// ...
}
The second approach is to declare TraitB a supertrait of TraitA. This will work even if TraitB is not defined in the same crate, however this will require any trait that implements TraitA to also implement TraitB, which again due to the orphan rule may not be possible for types that are not defined in the same crate.
trait TraitA: TraitB {
// ...
}
impl TraitA for SomeType {}
// required by the above, else the compiler will complain
impl TraitB for SomeType {}
In either case, you will not be able to implement a trait from a different crate, such as Display, on a type from a different crate. The first approach won't work for your code, but the second can work since the Car type is defined in the same crate.
An approach to circumvent the issue entirely is instead to have a "displayable wrapper type" that can wrap any type that implements Vehicle. For example:
struct DisplayVehicle<'a, V: ?Sized + Vehicle>(&'a V);
impl<'a, V: ?Sized + Vehicle> std::fmt::Display for DisplayVehicle<'a, V> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Has {} wheels", self.0.get_num_wheels())
}
}
fn main() {
let car = Car {};
println!("{}", DisplayVehicle(&car));
}
(Playground link)
While this does become slightly more verbose, it avoids the orphan rule entirely and therefore doesn't have the same issues as trying to implement Display directly on every Vehicle type. Additionally, since the Display implementation isn't actually related to the type itself but to the trait, this may be generally a more idiomatic approach to the solve this problem.
It works if you cast directly to the trait object btw:
trait Vehicle {
fn get_num_wheels(&self) -> u32;
}
impl std::fmt::Display for dyn Vehicle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Has {} wheels", self.get_num_wheels())
}
}
struct Car();
impl Vehicle for Car {
fn get_num_wheels(&self) -> u32 {
4
}
}
fn main() {
let car = Car {};
println!("{}", &car as &dyn Vehicle);
}
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=34f17655e85b3039c327846f5b8a9568
I define a struct MyData and implement PartialEq and Hash traits for it manually.
I define a enum which includes Rc<MyData> and Rc<RefCell<MyData>>.
I want derive PartialEq and Hash for the enum, but fails:
The PartialEq and Hash both work for Rc<MyData>;
The PartialEq works for Rc<RefCell<MyData>> too;
But the Hash does not work for Rc<RefCell<MyData>> !
I have 2 questions:
Why? Why only the Hash does not work only for Rc<RefCell<MyData>>?
How to fix it?
I can not implement Hash for Rc<RefCell<MyData>>. After searching around I find a way: defining a new wrapper struct, like struct RRWrapper<T> (Rc<RefCell<T>>), and then implement Hash for this RRWrapper. But this will bring much code. Is there an idiomatic way? I think this's a general usage.
Thank in advance,
Wu
PS: In the real code of my program, there is only Rc<RefCell<MyData>> in the enum but no Rc<MyData>. I put Rc<MyData> here just for comparision.
PS2: In the real code of my program, there are more than one Rc<RefCell<T>> in the enum.
Original source code:
use std::rc::Rc;
use std::cell::RefCell;
use std::hash::{Hash, Hasher};
struct MyData {
i: i64,
}
impl Hash for MyData {
fn hash<H: Hasher>(&self, state: &mut H) {
self.hash(state);
}
}
impl PartialEq for MyData {
fn eq(&self, other: &Self) -> bool {
self == other
}
}
#[derive(PartialEq, Hash)]
enum MyEnum {
INT(i64),
STR(String),
MYDATA1(Rc<MyData>), // OK both
MYDATA2(Rc<RefCell<MyData>>), // OK for PartialEq but not for Hash
}
fn main() {
}
Error:
20 | #[derive(PartialEq, Hash)]
| ---- in this derive macro expansion
...
25 | MYDATA2(Rc<RefCell<MyData>>), // OK for PartialEq but not for Hash
| ^^^^^^^^^^^^^^^^^^^ the trait `Hash` is not implemented for `RefCell<MyData>`
|
= note: required because of the requirements on the impl of `Hash` for `Rc<RefCell<MyData>>`
= note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info)
Source code of struct RRWrapper:
#[derive(Debug, PartialEq, Eq)]
pub struct RRWrapper<T: Hash+PartialEq+Eq>(Rc<RefCell<T>>);
impl<T: Hash+PartialEq+Eq> Deref for RRWrapper<T> {
type Target = Rc<RefCell<T>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T: Hash+PartialEq+Eq> Hash for RRWrapper<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.borrow().hash(state);
}
}
impl<T: Hash+PartialEq+Eq> Clone for RRWrapper<T> {
fn clone(&self) -> Self {
RRWrapper(self.0.clone())
}
}
impl<T: Hash+PartialEq+Eq> RRWrapper<T> {
pub fn new(inner: T) -> Self {
RRWrapper(Rc::new(RefCell::new(inner)))
}
}
Why? Why only the Hash does not work only for Rc<RefCell>?
If you think about it it makes sense that Hash is not implemented for RefCell. Since it is an abstraction for interior mutability, what could be the hash of something that may change? In general, mutable things are not suitable to work as Hash objects because of that.
How to fix it?
Exactly as you did. Taking responsibility in what you exactly want. Implementing it for a wrapper.
I would like to implement Borrow for UserFriendlyDataStructure to provide access to the internal_data field within a function that should be agnostic with respect to the data provider. The type of the internal_data field is determined by the type associated to trait TraitA. Note that the Sealed trait ensures that none of these traits here can be implemented by other crates; this is functionality that strictly I provide. Furthermore, the type TraitA::Data is restricted by the empty trait DataTrait to prevent UserFriendlyDataStructure from being used as that type.
The following example explains best:
use std::borrow::Borrow;
use std::marker::PhantomData;
mod private {
pub trait Sealed {}
}
pub trait DataTrait: private::Sealed {}
pub trait TraitA: private::Sealed {
type Data: DataTrait;
}
pub struct UserFriendlyDataStructure<A: TraitA> {
internal_data: A::Data,
_a: PhantomData<A>,
}
impl<A: TraitA> Borrow<A::Data> for UserFriendlyDataStructure<A> {
fn borrow(&self) -> &A::Data {
&self.internal_data
}
}
pub fn important_function<A: TraitA, T: Borrow<A::Data>>(data: &T) {
let _internal_data = data.borrow();
// Do lots of work.
}
#[cfg(test)]
mod tests {
use super::*;
pub struct TestData(u32);
impl super::private::Sealed for TestData {}
impl DataTrait for TestData {}
pub struct TestProvider;
impl super::private::Sealed for TestProvider {}
impl TraitA for TestProvider {
type Data = TestData;
}
#[test]
fn basic_test() {
let ufds: UserFriendlyDataStructure<TestProvider> = UserFriendlyDataStructure {
internal_data: TestData(100),
_a: PhantomData::default(),
};
important_function::<TestProvider, _>(&ufds);
}
}
Unfortunately, the compiler complains:
error[E0119]: conflicting implementations of trait `std::borrow::Borrow<UserFriendlyDataStructure<_>>` for type `UserFriendlyDataStructure<_>`:
--> src/lib.rs:19:1
|
19 | impl<A: TraitA> Borrow<A::Data> for UserFriendlyDataStructure<A> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: conflicting implementation in crate `core`:
- impl<T> std::borrow::Borrow<T> for T
where T: ?Sized;
Is there a way to achieve what I am trying to do?
The compiler can be cajoled into accepting the code by introducing a redundant second type parameter that is constrained to be identical to A::Data:
impl<A, D> Borrow<D> for UserFriendlyDataStructure<A>
where
A: TraitA<Data = D>,
D: DataTrait,
{
fn borrow(&self) -> &A::Data {
&self.internal_data
}
}
I don't know why this works, and simply constraining A::Data: DataTrait doesn't. I think the compiler should accept both versions.
(Full code on the playground)
Edit: The fact that we need the redundant type D in the above code appears to be a shortcoming of the current compiler implementation, and is hopefully resolved once the experimental type inference engine chalk gets integrated in the compiler.
use std::collections::HashMap;
// #[derive(Copy, Clone)]
enum SomeEnum {
Some1,
Some2,
}
struct SomeStruct {
pub some_enum: SomeEnum,
pub s: String,
}
fn proc_struct(some_struct: &SomeStruct) {
let mut map = HashMap::new();
map.insert(String::from("any"), 0);
match map.get(&some_struct.s) { // just to make a reference in SomeStruct
Some(v) => {
proc1(some_struct.some_enum);
proc2(&some_struct.some_enum);
}
None => {}
}
}
fn proc1(some: SomeEnum) {}
fn proc2(some: &SomeEnum) {}
fn main() {
let some_struct = SomeStruct { some_enum: SomeEnum::Some1, s: String::from("s") };
proc_struct(&some_struct);
}
The above code produces the following error:
error[E0507]: cannot move out of borrowed content
--> src/main.rs:20:19
|
20 | proc1(some_struct.some_enum);
| ^^^^^^^^^^^ cannot move out of borrowed content
When I add #[derive(Copy, Clone)] to SomeEnum, it compiles fine.
Should simple enums like SomeEnum derive the Copy trait?
Is there any difference in performance between functions proc1() and proc2()?
Citing the docs:
Generally speaking, if your type can implement Copy, it should.
Since your SomeEnum has no composite variants (e.g. Vecs or Strings), I recommend making it Copyable. It appears that it is actually smaller than a reference to it:
use std::mem;
enum SomeEnum {
Some1,
Some2,
}
fn main() {
assert_eq!(1, mem::size_of::<SomeEnum>());
assert_eq!(8, mem::size_of::<&SomeEnum>());
}
Though I doubt that in this case you will be able to detect any difference in performance.
I'm trying to do the equivalent of Ruby's Enumerable.collect() in Rust.
I have an Option<Vec<Attachment>> and I want to create a Option<Vec<String>> from it, with String::new() elements in case of None guid.
#[derive(Debug)]
pub struct Attachment {
pub guid: Option<String>,
}
fn main() {
let ov: Option<Vec<Attachment>> =
Some(vec![Attachment { guid: Some("rere34r34r34r34".to_string()) },
Attachment { guid: Some("5345345534rtyr5345".to_string()) }]);
let foo: Option<Vec<String>> = match ov {
Some(x) => {
x.iter()
.map(|&attachment| attachment.guid.unwrap_or(String::new()))
.collect()
}
None => None,
};
}
The error in the compiler is clear:
error[E0277]: the trait bound `std::option::Option<std::vec::Vec<std::string::String>>: std::iter::FromIterator<std::string::String>` is not satisfied
--> src/main.rs:15:18
|
15 | .collect()
| ^^^^^^^ the trait `std::iter::FromIterator<std::string::String>` is not implemented for `std::option::Option<std::vec::Vec<std::string::String>>`
|
= note: a collection of type `std::option::Option<std::vec::Vec<std::string::String>>` cannot be built from an iterator over elements of type `std::string::String`
If I remember what I've read from the documentation so far, I cannot implement traits for struct that I don't own.
How can I do this using iter().map(...).collect() or maybe another way?
You should read and memorize all of the methods on Option (and Result). These are used so pervasively in Rust that knowing what is present will help you immensely.
For example, your match statement is Option::map.
Since you never said you couldn't transfer ownership of the Strings, I'd just do that. This will avoid any extra allocation:
let foo: Option<Vec<_>> =
ov.map(|i| i.into_iter().map(|a| a.guid.unwrap_or_else(String::new)).collect());
Note we don't have to specify the type inside the Vec; it can be inferred.
You can of course introduce functions to make it cleaner:
impl Attachment {
fn into_guid(self) -> String {
self.guid.unwrap_or_else(String::new)
}
}
// ...
let foo: Option<Vec<_>> = ov.map(|i| i.into_iter().map(Attachment::into_guid).collect());
If you don't want to give up ownership of the String, you can do the same concept but with a string slice:
impl Attachment {
fn guid(&self) -> &str {
self.guid.as_ref().map_or("", String::as_str)
}
}
// ...
let foo: Option<Vec<_>> = ov.as_ref().map(|i| i.iter().map(|a| a.guid().to_owned()).collect());
Here, we have to use Option::as_ref to avoid moving the guid out of the Attachment, then convert to a &str with String::as_str, providing a default value. We likewise don't take ownership of the Option of ov, and thus need to iterate over references, and ultimately allocate new Strings with ToOwned.
Here is a solution that works:
#[derive(Debug)]
pub struct Attachment {
pub guid: Option<String>,
}
fn main() {
let ov: Option<Vec<Attachment>> =
Some(vec![Attachment { guid: Some("rere34r34r34r34".to_string()) },
Attachment { guid: Some("5345345534rtyr5345".to_string()) }]);
let foo: Option<Vec<_>> = ov.map(|x|
x.iter().map(|a| a.guid.as_ref().unwrap_or(&String::new()).clone()).collect());
println!("{:?}", foo);
}
One of the issues with the above code is stopping the guid being moved out of the Attachment and into the vector. My example calls clone to move cloned instances into the vector.
This works, but I think it looks nicer wrapped in a trait impl for Option<T>. Perhaps this is a better ... option ...:
trait CloneOr<T, U>
where U: Into<T>,
T: Clone
{
fn clone_or(&self, other: U) -> T;
}
impl<T, U> CloneOr<T, U> for Option<T>
where U: Into<T>,
T: Clone
{
fn clone_or(&self, other: U) -> T {
self.as_ref().unwrap_or(&other.into()).clone()
}
}
#[derive(Debug)]
pub struct Attachment {
pub guid: Option<String>,
}
fn main() {
let ov: Option<Vec<Attachment>> =
Some(vec![Attachment { guid: Some("rere34r34r34r34".to_string()) },
Attachment { guid: Some("5345345534rtyr5345".to_string()) },
Attachment { guid: None }]);
let foo: Option<Vec<_>> =
ov.map(|x| x.iter().map(|a| a.guid.clone_or("")).collect());
println!("{:?}", foo);
}
Essentially the unwrapping and cloning is hidden behind a trait implementation that attaches to Option<T>.
Here it is running on the playground.