For example:
impl<T: ?Sized> cmp::Eq for $t<T> {}
Why would you even write this code if it has no implementation? It happens in many places, especially related to Eq.
#[unstable(feature = "structural_match", issue = "31434")]
impl<T: ?Sized> StructuralPartialEq for $t<T> {}
#[unstable(feature = "structural_match", issue = "31434")]
impl<T: ?Sized> StructuralEq for $t<T> {}
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> Copy for $t<T> {}
impl<T: ?Sized> !Freeze for UnsafeCell<T> {}
unsafe impl<T: ?Sized> Freeze for PhantomData<T> {}
unsafe impl<T: ?Sized> Freeze for *const T {}
unsafe impl<T: ?Sized> Freeze for *mut T {}
unsafe impl<T: ?Sized> Freeze for &T {}
unsafe impl<T: ?Sized> Freeze for &mut T {}
A trait can be implemented with an empty impl {} block if it has no methods, or if all of its methods have default implementations. These are called "marker" or "tag" traits.
Abstraction without overhead: traits in Rust has a description of marker traits:
Rust has a handful of "markers" that classify types: Send, Sync, Copy, Sized. These markers are just traits with empty bodies, which can then be used in both generics and trait objects. Markers can be defined in libraries, and they automatically provide #[derive]-style implementations: if all of a types components are Send, for example, so is the type. As we saw before, these markers can be very powerful: the Send marker is how Rust guarantees thread safety.
Eq is another common marker trait. It's a tag for types that already implement PartialEq, indicating that the equality operation is not in fact "partial"; it is fully reflexive, symmetric, and transitive. Implementing it doesn't require any additional implementation since the eq implementation is taken from the type's impl PartialEq { ... } block.
pub trait Eq: PartialEq<Self> { }
Trait for equality comparisons which are equivalence relations.
This means, that in addition to a == b and a != b being strict inverses, the equality must be (for all a, b and c):
reflexive: a == a;
symmetric: a == b implies b == a; and
transitive: a == b and b == c implies a == c.
This property cannot be checked by the compiler, and therefore Eq implies PartialEq, and has no extra methods.
Derivable
This trait can be used with #[derive]. When derived, because Eq has no extra methods, it is only informing the compiler that this is an equivalence relation rather than a partial equivalence relation. Note that the derive strategy requires all fields are Eq, which isn’t always desired.
How can I implement Eq?
If you cannot use the derive strategy, specify that your type implements Eq, which has no methods:
enum BookFormat { Paperback, Hardback, Ebook }
struct Book {
isbn: i32,
format: BookFormat,
}
impl PartialEq for Book {
fn eq(&self, other: &Self) -> bool {
self.isbn == other.isbn
}
}
impl Eq for Book {}
Not every trait has methods you need to define. Some have methods that all come with default implementations, others have no methods at all.
However, Rust still needs to be explicitly told that any given type implements a trait. Rust does not do "duck typing" (such as in golang) where traits are implicitly implemented if the right method signatures exist, you always have to do implement a trait explicitly. There are good design reasons for this, but perhaps that can be researched separately if you're interested.
Thus, for implementing traits either by using only default method implementations, or a trait that has no methods at all, you still need to write the impl block. Of course, that impl block will be empty in those cases, since all it's saying is "struct Foo implements trait Bar", with no further information required for the compiler.
There are lots of good reasons to write traits that don't require methods to be defined. A common one is "marker traits" which simply mark something about what the type is/how it behaves/what properties it has, but doesn't require any methods to evidence this. Another fairly common one is sealed traits which allows you to have a pub trait (so your users can see and use the methods on) where nobody else can impl it outside of your crate (so you can guarantee that any type that implements that trait is one you control).
Related
I'm using the open source rust pcap library (https://github.com/rust-pcap/pcap) whose main Capture struct uses a system of market traits to denote which functions are available during which lifecycle state/phase of the capture (e.g., you can't 'sendpacket' on a Capture in the "DEAD" state). It has a hierarchy of State traits such that, e.g., "Activated" is a trait that is implemented by both the "Active", "Dead" and "Offline" states:
pub trait Activated: State {}
impl Activated for Active {}
impl Activated for Offline {}
impl Activated for Dead {}
pub trait State {}
impl State for Inactive {}
impl State for Active {}
impl State for Offline {}
impl State for Dead {}
pub struct Capture<T: State + ?Sized> {
...
}
With that code, I am trying unsuccessfully to convert from a parameterized+bounded trait as a <T: Activated + ?Sized> to a Capture, e.g., :
fn foo<T: Activated + ?Sized>(&mut capture: Capture<T>) {
bar(capture as Capture<dyn Activated>);
}
fn bar(&mut capture: Capture<dyn Activated>) {
...
}
... but am getting an error:
non-primitive cast: &mut pcap::Capture<T> as &mut pcap::Capture<dyn pcap::Activated> as expression can only be used to convert between primitive types or to coerce to a specific trait object
... which I don't understand at all, despite a fair bit of reading on this. Can someone please explain to me how to do this (or why it's wrong to think this should work)? Thank you in advance!
In current Rust, you can only coerce from Foo<T> to Foo<dyn Trait> if T is a sized type. This is because the conversion requires attaching a vtable pointer for Trait, and said vtable is only available when T is a regular, non-dyn type. So, this code,
fn foo<T: Activated + ?Sized>(&mut capture: Capture<T>) {
bar(capture as Capture<dyn Activated>);
}
will always fail to compile because T: ?Sized, meaning that T might be already a dyn, and you can't cast from a dyn to an arbitrary different dyn.
There is an unstable feature, trait_upcasting (#65991) that will enable dyn conversions to supertraits, like Capture<dyn Activated> to Capture<dyn State>, but that still does not include conversion from an arbitrary T: ?Sized. (In fact, if T = [Foo], a type that is dynamically sized for a different reason, then it's impossible for an entirely different reason than traits — the compiler does not support a type being simultaneously vtable-based (having a pointer) and a slice (having a length). That restriction could in principle be lifted, but it'd be more new territory.)
To recap: You will need to organize your program so that you do not need to convert a ?Sized type to a trait object, because that is not possible.
The code snippet below comes from substrate https://github.com/paritytech/substrate/blob/master/client/state-db/src/lib.rs.
What does it do? How can you implement a trait Hash for a generic type T? Commenting out the impl <...> and the compiler complains "the trait Hash is not implemented for Vec<u8>", but how that empty piece of code (impl<...>) implements Hash for Vec<u8>?
pub trait Hash:
Send
+ Sync
+ Sized
+ Eq
+ PartialEq
+ Clone
+ Default
+ fmt::Debug
+ Codec
+ std::hash::Hash
+ 'static
{
}
impl<
T: Send
+ Sync
+ Sized
+ Eq
+ PartialEq
+ Clone
+ Default
+ fmt::Debug
+ Codec
+ std::hash::Hash
+ 'static,
> Hash for T
{
}
What does it do? How can you implement a trait Hash for a generic type T?
Like that? It's called a "blanket implementation", and implements the trait for all types (usually with specific generic bounds, but not necessarily).
how that empty piece of code (impl<...>) implements Hash for Vec?
Hash is a marker trait, it doesn't contain anything, it just opts into some sort of capability (the most well known marker traits in Rust are Sized, Send, and Sync). So to implement it you just needs to say impl Hash for Type.
But you could certainly do that with a non-marker trait as well, as long as you only used contents from the traits you defined as trait bounds. For instance the FutureExt trait is blanket-implemented on all types which implement Future and adds a bunch of utility methods. That's a common pattern, with a conventional RFC explaining the pattern and why you'd want to apply it.
Rust allows you to implement your traits for any types, including other people's types. To help with this Rust allows implementing traits for (partially) generic types, as long as it's your trait (see orphan rules).
What might be confusing in this context is that Hash also exists as std::hash::Hash. But these two are not the same trait, despite having the same name. It would be easier to understand if we renamed it to Foo, and focused on, say, PartialEq + Eq.
// We make a new trait named Foo and say that any type implementing Foo
// must also implement PartialEq and Eq.
trait Foo : PartialEq + Eq { }
// For any type implementing PartialEq and Eq, implement Foo for that type.
// This is allowed because we also created Foo.
impl<T: PartialEq + Eq> impl Foo for T { }
This
'trick' is often used to combine many traits (like in your Hash example) into a single name (called a marker trait). But it's not limited to that, for example you could make the following trait:
trait Reset: Default {
fn reset(&mut self);
}
impl<T: Default> Reset for T {
fn reset(&mut self) {
*self = Self::default();
}
}
Now any type that implements Default will have a reset() method that returns it to the default state thanks to our generic implementation (also called a blanket implementation), as long as you have the Reset trait in scope.
Hash is an empty trait with no methods. It inherits a lot of other traits so any struct you impl it for needs to impl all the traits it inherits from.
So if you have a generic <T : Trait1 + Trait2 +...> the compiler knows that type T satisfies all the trait dependencies for trait Hash
So what this code does is impl Hash for any T that implements all the inherited traits
On debugging (step-in) this Rust code:
use std::fs;
fn main() {
fs::create_dir("my_dir").unwrap();
}
I go to an impl I feel hard to understand:
// rustup\toolchains\stable-x86_64-pc-windows-gnu\lib\rustlib\src\rust\library\core\src\convert\mod.rs
// As lifts over &
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized, U: ?Sized> AsRef<U> for &T
where
T: AsRef<U>,
{
fn as_ref(&self) -> &U {
<T as AsRef<U>>::as_ref(*self)
}
}
Which then calls:
#[stable(feature = "rust1", since = "1.0.0")]
impl AsRef<Path> for str {
#[inline]
fn as_ref(&self) -> &Path {
Path::new(self)
}
}
What does this "As lifts over &" impl mean? Why it is needed?
This piece of code:
<T as AsRef<U>>
I've never seen this syntax before. What does it mean?
Seems like there is a lot of implicit conversions like this done by Rust compiler behind our daily Rust code. Is there any way to learn them besides debugging?
I assume As lifts over & simply means that if T can be taken as a reference to U, so does &T, because why not?
It also gives some convenience, e.g. if you happened to have a value of type &&&&&&T, you won't need to (******t).as_ref(), it's just t.as_ref(), because by using such implementation, as_ref goes through all levels of references up to the T object itself and takes it as a reference to U.
How can you learn it? Well, the top 3 most popular Rust textbooks have it in some way (not necessarily explicitly explaining you this particular case, but giving you enough knowledge to understand them implicitly. So I advice you to read one (and read it more carefully).
About the <T as AsRef<U>>, it is definitely covered in the books. It's needed to disambiguate calls to functions. For example, if some type implements two different traits, but both traits have the same method as_ref, then you'll need to fully qualify function of which trait you mean to call, for this you <T as AsRef<U>> and it's immediately clear which as_ref function you call.
Lets say you have 2 traits that share an item and a type that implements both. You can use this syntax to specify which implementation to use.
pub trait Foo {
const BAZ: usize;
}
pub trait Bar {
const BAZ: usize;
}
pub fn thing<T: Foo + Bar>() {
// This wont compile since it doesn't know which BAZ to use
println!("Unknown: {}", T::BAZ);
// You can use <a as b>::c to further specify which to use
println!("As Foo: {}", <T as Foo>::BAZ);
println!("As Bar: {}", <T as Bar>::BAZ);
}
You can read <A as B>::C as follows where C can be anything inside a trait (constant, function, type, etc.). It can also be used to make your code more verbose. If it is unclear which trait is being used, this can help make your code more explicit.
"Interpret type A as an instance of trait B for the purpose of retrieving C"
There are 2 ways to provide methods for a Trait itself, Rustdoc distinguishes them by saying "provided methods" and impl dyn XXX. For example:
trait Trait {
fn foo(&self) {
println!("Default implementation");
}
}
impl Trait {
fn bar(&self) {
println!("Anonymous implementation?");
}
}
I noticed it when I was reading the documentation of Rust's failure crate.
What are the use cases for them? What are the differences?
The first snippet,
trait Trait {
fn foo(&self) {
println!("Default implementation");
}
}
implements a provided method on the trait. This method can be overridden by a trait implementation, but it does not have to be overridden.
The second snippet,
impl Trait {
fn bar(&self) {
println!("Anonymous implementation?");
}
}
implements an inherent method on a trait object of type dyn Trait. Method implementations for dyn Trait can only be called for trait objects, e.g. of type &dyn Trait. They can't receive self by value, since dyn Trait does not have a size known at compile time, and they can't be called on concrete types implementing Trait (including generic types with a Trait bound).
The modern notation is to write impl dyn Trait instead of impl Trait, and in fact this notation was one of the motivating examples for the introduction of the dyn keyword – the old syntax did not provide any clues as to what the semantics are, whereas the new syntax with the dyn keyword hints at the fact that this impl is only used together with dynamic dispatch.
A trait object is a fat pointer to an object implementing Trait, but the concrete type of the object is not necessarily known at compile time. The fat pointer contains a pointer to the object data, as well as a pointer to the virtual method table of the object type. The latter is used to dynamically dispatch to the correct trait implementation at runtime.
It is rather uncommon to use impl dyn Trait. Generally it's only useful if you want to make use of some dynamic type information, like downcasting to the actual type. The only traits with inherent methods on trait objects in the standard library are Any and Error.
In short: one can be overridden, and the other cannot.
When you define a trait, you define items that implementations of the trait may (or have to) override:
trait Trait {
fn foo(&self) {
println!("Default implementation");
}
}
impl Trait for i64 {
fn foo(&self) {
println!("i64 implementation: {}", self);
}
}
On the other hand, using impl Trait, you define inherent methods, which cannot be overridden:
impl Trait {
fn bar(&self) {
self.foo();
self.foo()
}
}
// Try:
impl Trait for i64 {
fn bar(&self) { ... } // error: bar cannot be overridden.
}
As a result, inherent traits methods act as the Template Method Pattern: they provide a canvas linking together one or multiple overridable method(s).
If you look at the failure crate that you linked, the method Failure::find_root_cause() states:
This is equivalent to iterating over iter_causes() and taking the last item.
You may consider those inherent methods to be convenience methods, methods providing an easy/intuitive interface for common tasks which can be accomplished manually... but are conveniently pre-defined.
Note: any inherent method could be implemented as a free function taking the trait as a first argument; however free functions cannot be called in method position.
I have a trait which only has one requirement, the presence of a methods len(&self) -> usize. It then provides implementations for a couple of new methods for the struct.
trait MyTrait {
fn len(&self) -> usize;
// ... some new functions with implementations
}
I'd like to implement this trait on std::collections::LinkedList which already has a method with that exact type signature. Currently, I'm doing this:
impl<T> MyTrait for LinkedList<T> {
fn len(&self) -> usize {
self.len()
}
}
I can then use the new methods defined by MyTrait on my LinkedList instances. However, it feels unnecessary to have to repeat the definition of the method like this, since the type signatures are identical. Is there a way to omit the re-definition of len in my trait implementation?
Is there a way to omit the re-definition of len in my trait implementation?
No, it is not possible.
You may be able to write a macro of some kind to write the code for you, but dealing with type parameters is annoying, to say the least.
See also:
How to call a method when a trait and struct use the same name?