Exclude type from trait bounds - rust

While playing around with Rust and its generics I came along some problem for which I cannot find any documentation.
I have implemented a type Wrapper which wraps another type. At some point I wanted to implement the From trait.
impl<TSrc, TDst> From<Wrapper<TSrc>> for Wrapper<TDst>
where
TSrc: From<TDst>
{
fn from(other: Wrapper<TSrc>) -> Self {
todo!()
}
}
rustc complains with following error
58 | impl<TSrc, TDst> From<Wrapper<TSrc>> for Wrapper<TDst>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: conflicting implementation in crate `core`:
- impl<T> From<T> for T;
This makes sense if TSrc and TDst are the same. Is it somehow possible to explicitly exclude TSrc from TDst?

No, it is not possible to exclude specific types in a generic implementation.
There's not really a general workaround beyond "do something else" which would typically look like an inherent conversion method, using a different (custom) trait, or allowing another mechanism for users to access the inner values and do the conversion themselves.
There may be mechanisms in the future like specialization or negative impls that could allow such an implementation (or similar), but nothing is on the horizon.
See also:
How is there a conflicting implementation of `From` when using a generic type?
Why do blanket implementations for two different traits conflict?
Is it possible to exclude a type from generic trait implementation?

Related

Using 'as T' in function type signature

I'm new to Rust but I'm already familiar with Generics. At least that's what I thought until I came across this trait definition (edited for brevity):
pub trait Queue<Item: ItemT>: Core<Item> {
fn validate(
&self,
hash: <Item as ItemT>::Hash
) -> bool;
}
What's this Item as ItemT thing? Isn't Item already of type ItemT? I'm also perplexed by the syntax <Item as ItemT>::Hash ... what's that supposed to be? Is it the same as ItemT::Hash? If yes, then why not say so?
Also, incidentally, does the syntax : Core<Item> mean that the Queue trait requires all implementors to also implement the Core trait? Or does it mean something else? And why would you do that? Who cares about Core? If somebody needs the Core trait, can't they just say so?
The syntax <Type as Trait>::Item is the fully-qualified syntax used to explicitly refer to an associated Item of a Trait on a Type. This is not terribly common to write out yourself, but you may need to if there is an ambiguity in what Item means for a Type.
Consider this modified example using Self::Hash where the current trait and supertrait both have a Hash associated type:
trait Core<Item> {
type Hash;
}
trait Queue<Item>: Core<Item> {
type Hash;
fn validate(
&self,
hash: Self::Hash
) -> bool;
}
error[E0221]: ambiguous associated type `Hash` in bounds of `Self`
--> src/lib.rs:9:15
|
2 | type Hash;
| ---------- ambiguous `Hash` from `Core<Item>`
...
6 | type Hash;
| ---------- ambiguous `Hash` from `Queue<Item>`
...
9 | hash: Self::Hash
| ^^^^^^^^^^ ambiguous associated type `Hash`
|
help: use fully qualified syntax to disambiguate
|
9 | hash: <Self as Queue<Item>>::Hash
| ~~~~~~~~~~~~~~~~~~~~~~~
help: use fully qualified syntax to disambiguate
|
9 | hash: <Self as Core<Item>>::Hash
| ~~~~~~~~~~~~~~~~~~~~~~
In this case, it is ambiguous what Self::Hash means, so the compiler requires that you fully-qualify what trait to use.
You may also see this syntax used in generated documentation. As an example, take the str::rmatches function. The original signature is written like so:
pub fn rmatches<'a, P>(&'a self, pat: P) -> RMatches<'a, P>
where
P: Pattern<'a, Searcher: ReverseSearcher<'a>>
But it shows up in the documentation like this:
pub fn rmatches<'a, P>(&'a self, pat: P) -> RMatches<'a, P>
where
P: Pattern<'a>,
<P as Pattern<'a>>::Searcher: ReverseSearcher<'a>,
Not exactly sure why this is; perhaps the designers thought disambiguation was a good idea by default, it was simpler this way, or if it was just better for generating links. Either way, its something you'll come across so its better to learn it and not get confused later.
In your particular example, I don't see why Item::Hash would need to be qualified, but perhaps there is more at play than what is shown, could simply be a fluke, or maybe its just an artifact caused by a previous version of the code.
Also, incidentally, does the syntax : Core<Item> mean that the Queue trait requires all implementors to also implement the Core trait? Or does it mean something else? And why would you do that? Who cares about Core? If somebody needs the Core trait, can't they just say so?
Yes, this is constraining the Queue<Item> trait that any implementation must also implement Core<Item>. Core<Item> is also called a supertrait with this usage.
This snippet is a bit too reduced to definitively say why that would be desired, but consider something like DoubleEndedIterator which has Iterator as a supertrait since it makes no sense for something to be a double-ended iterator but not be an iterator. So its often used in similar scenarios where it makes no sense to use use the subtrait independently.
But your instincts are good; if a trait can reasonably be used without the supertrait, then it shouldn't.
What's this Item as ItemT thing?
<Item as ItemT>:: is how you specify associated item (type, method, constant) from the implementation of the trait ItemT for the type Item.
For a less complex example, suppose we have some type Foo that implements the Default trait. Then the following are all ways of calling the same function:
let a: Foo = Default::default();
let b = Foo::default();
let c = <Foo as Default>::default();
and their particular characteristics are that:
a depends on having the result type specified to infer which type should be used with the Default trait.
b might be referring to an inherent (non-trait) associated function of Foo, rather than the one from Default, if someone wrote
impl Foo { fn default() { ... } }
c is fully specified.
I'm also perplexed by the syntax <Item as ItemT>::Hash ... what's that supposed to be? Is it the same as ItemT::Hash?
Yes.
If yes, then why not say so?
I don't know the exact logic, but often the compiler requires you to specify which trait (ItemT) the associated type (Hash) came from — even if you might think it was obvious. I presume this is in order to avoid future ambiguity if additional traits define associated types that might have the same name Hash.
does the syntax : Core<Item> mean that the Queue trait requires all implementors to also implement the Core trait?
Yes, precisely so. This syntax is called supertraits.
And why would you do that? Who cares about Core? If somebody needs the Core trait, can't they just say so?
In some cases, trait bounds would get very verbose if every required trait had to be spelled out for every generic function. But more generally, it's used when the requirement is a logical necessity, “it cannot possibly make sense to implement trait A and not implement trait B”, or at least when that would be very impractical (the library design chooses not to support the cases where B would not be implemented).

What are "Blanket Implementations" in Rust?

When looking through the documentation of Rust structs, I often come across a section with the title "Blanket Implementations". I have heard that it can be used to implement a trait for all types or all types that match some condition but I am not sure why this is needed.
So what really are Blanket Implementations and why are they useful in Rust?
A blanket implementation is an implementation of a trait on a generic parameter:
impl<T> Trait for T
They usually also have where clauses involved since it is very hard to do anything useful to an unconstrained T. This does not cover things like impl<T> Trait for Vec<T>, which is a generic implementation but not a blanket implementation.
They are documented separately since they are applied without any particularity and may or may not be relevant to the type you're looking at. Whereas for the normal "Trait Implementations" section, all those traits at least had some thought for the specific type in mind (usually).
They are patently useful since it implements the trait for anything, in the entire ecosystem! If something satisfies the constraints, then it is able to take advantage of the implementation without needing to implement it themselves. You do not need to do anything to "opt-in" besides bringing the trait into scope.
Some notable ones:
From<T> is implemented for all T (the identity implementation)
Into<U> is implemented for all T where T: From<U> (the reflexive implementation that allows you to call .into() when a matching From implementation exists)
Any is implemented for all T where T: 'static
They are also needed to implement "trait aliases" where you create a trait that is constrained over multiple other traits (handy for reducing boilerplate and needed for multi-trait trait objects). You use a blanket implementation that the trait is implemented for any type satisfying the super-traits:
trait MyCoolAsyncTrait: AsyncRead + AsyncWrite + AsyncSeek + 'static {}
impl<T> MyCoolAsyncTrait for T
where
T: AsyncRead + AsyncWrite + AsyncSeek + 'static
{ }
Be careful when adding them to your types though. Because of their extensive scope, they can easily conflict with other trait implementations that may be desirable. You can only define one blanket implementation per type (even if the constraints are non-overlapping).
See also:
What are blanket implementations? (Rust forum)
Traits: Defining Shared Behavior in the Rust book
Is there any way to create a type alias for multiple traits?
How is there a conflicting implementation of `From` when using a generic type?
Why do blanket implementations for two different traits conflict?

How to tell rust to do all float comparisons using a given lib by default?

I would like to have all float comparisons done with float_cmp::approx_eq (for example), but keep using the equality comparison operator == for it. How do I achieve this?
impl PartialEq for f32 {
fn eq(&self, other: &Self) -> bool {
approx_eq!(f32, *self, *other)
}
}
Results in:
error[E0119]: conflicting implementations of trait `std::cmp::PartialEq` for type `f32`
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
This is not possible:
This trait implementation is in direct conflict with an existing implementation. There is no trait overriding mechanism. In the future you may be able to specialize a generic trait implementation, but this wouldn't be covered by that anyway.
Rust has "orphan rules" that govern what trait implementations you are allowed to define. In short, some part of the trait or type must be defined by the current crate. Neither PartialEq and f32 are defined by you, so you cannot create this implementation.
The approx_eq! macro from float_cmp expands to code that uses == internally, so such implementation if it were allowed would cause infinite recursion.
I don't think there is a way to accomplish this and I'm not sure I'd suggest using it even if there were. This would affect all cases where float comparison is done, even deep in your dependencies that could end up causing problems. And this leaves little option to do non-approximate equality even if you explicitly wanted to.
You should handle cases where you only want to consider approximate equality explicitly.

Restrict a generic Type to primative number types [duplicate]

This question already has answers here:
Is there any way to restrict a generic type to one of several types?
(2 answers)
Closed 3 years ago.
We can restrict a type to one or more traits by where clause.
My questions are:
Is it possible to restrict a type to just primitive number types?
How?
No.
To use a parametric type, you need the trait to define the valid operations you want to call on it. So you need a trait (or more) with all the operations you want to call.
The “primitive” types in Rust are not special in any way. They define their operators via the traits from std::ops (though obviously using compiler intrinsics) just like any “non-primitive” numberic types.
In fact, the boundary between “primitive” and “non-primitive” numeric types is even somewhat blurred, since for targets that lack an FPU, the standard library may be implementing floating point types in code, and in Rust it can do it transparently to the user.
So really, there is no such thing as primitive number types. Number types are defined by providing whatever operators you need to call. So just restrict your type by the std::ops traits.
Since primitives are not trait types you cannot use as boundary to restrict a generic type. But you can directly implement for a specific type:
struct Struct<T>(T);
trait Printer {
fn print(&self);
}
impl Printer for Struct<i32> {
fn print(&self) {
println!("Printing for i32 value: {}", self.0);
}
}
fn main() {
let x = Struct(15_i32);
let _z = Struct(14.2_f64);
x.print();
//_z.print();//compile error
}
Playground
Alternatively you can use Borrow<S> trait as trick, you can restrict your generic parameter as like below : ( T: Borrow<S> means T can be borrowed as S ) .
impl<T> Printer for Struct<T>
where
T: Borrow<f64> + Debug,
{
fn print(&self) {
println!("Printing for f64 value: {:?}", self.0);
}
}
Playground
But since you can implement Borrow<f64> for any type, this kind of restriction may not be considered as strict.
Also just for the numeric primitives you can use traits from num-traits crate like ToPrimitive AsPrimitive
If you think about traits as compile time duck typing, then a better question would be: what are the exact traits you are looking for in a number? Most of the operations on them could be defined as trait constraints on your types, see: the standard operator traits.
Even if you would define a trait in an external crate and implement the behaviour for specific types only in that external crate, thinking that the trait implementation rules will help you there (i.e. a trait can only be implemented to a type if either the trait or the type or both are in your current crate) would still not limit anyone to implement your trait for their own types.
Therefore, I see no other option but to implement the behaviours without generics for each primitive number type. To avoid code duplication, I would probably use macros for this -- after all, if you think about it in some ways you would manually do what the compiler does while it monomorphises your generic code.
That being said I see no reason to limit behaviours to numbers but to certain traits and I would rely on them as I described it in the first paragraph.

Is there a way other than traits to add methods to a type I don't own?

I'm trying to extend the Grid struct from the piston-2dgraphics library. There's no method for getting the location on the window of a particular cell, so I implemented a trait to calculate that for me. Then, I wanted a method to calculate the neighbours of a particular cell on the grid, so I implemented another trait.
Something about this is ugly and feels unnecessary seeing as how I'll likely never use these traits for anything other than this specific grid structure. Is there another way in Rust to extend a type without having to implement traits each time?
As of Rust 1.67, no, there is no other way. It's not possible to define inherent methods on a type defined in another crate.
You can define your own trait with the methods you need, then implement that trait for an external type. This pattern is known as extension traits. The name of extension traits, by convention, ends with Ext, to indicate that this trait is not meant to be used as a generic bound or as a trait object. There are a few examples in the standard library.
trait DoubleExt {
fn double(&self) -> Self;
}
impl DoubleExt for i32 {
fn double(&self) -> Self {
*self * 2
}
}
fn main() {
let a = 42;
println!("{}", 42.double());
}
Other libraries can also export extension traits (example: byteorder). However, as for any other trait, you need to bring the trait's methods in scope with use SomethingExt;.
No. Currently, the only way to write new methods for a type that has been defined in another crate is through traits. However, this seems too cumbersome as you have to write both the trait definition and the implementation.
In my opinion, the way to go is to use free functions instead of methods. This would at least avoid the duplication caused by traits.

Resources