I am trying to extend the functionality of the Iterator trait.
My statistics/iter_statistics.rs:
mod iter_statistics {
pub trait IterStatistics: Iterator<Item = f64> {
fn foo(&mut self) -> f64 {
0.0
}
}
impl IterStatistics for Iterator<Item = f64> {}
}
And statistics/mod.rs:
pub use self::iter_statistics::*;
mod iter_statistics;
And finally in my test code I have
use statistics::IterStatistics;
fn main() {
let z: Vec<f64> = vec![0.0, 3.0, -2.0];
assert_eq!(z.into_iter().foo(), 0.0);
}
When I run the test, I get:
error: no method name `foo` found for type `std::vec::IntoIter<f64>` in the current scope
assert_eq!(z.into_iter().foo(), 0.0);
^~~
which is strange to me since the docs for IntoIter<T> say it implements Iterator<Item=T>.
The impl you have written will only apply to trait objects (e.g. &mut Iterator<Item=f64>), not for all types that implement Iterator<Item=f64>. You want to write a generic impl like this:
impl<T: Iterator<Item=f64>> IterStatistics for T {}
Your implementation is backward. When programming in Rust, you have to forget about OO-inheritance and reason in terms of capabilities.
What does trait D: B means in Rust?
This means that D can only be implemented for types that already implement B. It's not inheritance, it's a constraint.
When to use trait D: B then?
The main reason to use this constraint is when you wish to provide a default implementation of D methods that will require associated items (traits, constants, methods) from B.
In general, you do not want to add more constraints than strictly necessary, as your clients may wish to use this trait in ways you did not foresee.
The one exception is when creating a trait as a "bundle of constraints", so that you do not have type T: SomeTrait + SomeOtherTrait + Send for all the methods your are implementing. This "bundle of constraints" should be empty, then; it's not a functional trait after all, just an "alias".
So, how to extend Iterator?
First declare a new trait:
pub trait IterStatistics {
fn foo(&mut self) -> f64;
}
Then implement it for all types already implementing Iterator<Item = f64>:
impl<T> IterStatistics for T
where T: Iterator<Item = f64>
{
fn foo(&mut self) -> f64 {
0.0
}
}
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
Below I define three traits. For each trait, I
implement on &'a T where T implements the trait
implement on &'a usize.
In each case, however, I observe something slightly different.
Case 1
For the trait GetOneVer1 I observe no errors.
Case 2
For the trait GetOneVer2 I get an error:
conflicting implementations of trait `GetOneVer2<_>` for type `&usize`
downstream crates may implement trait `GetOneVer2<_>` for type `usize`rustcE0119
I think I understand this error -- the compiler is worried that in the future someone will implement the trait on usize and then that implementation will automatically extend to &'a usize because I implement the trait on &'a T where T implements the trait. That means we could potentially have two competing implementations of the same trait.
Case 3
For the trait GetOneVer3 I get no error. Note that this case is essentially identical to case 2. The only difference is that, in this case, I define an extra trait "InheritTrait" and require that T implements InheritTrait in my implementation on &'a T.
Questions
Why does case 2 produce errors whereas cases 1 and 3 do not? The only difference I see is that, in case 2, the trait has a generic type parameter, and in case 3 there are both a generic type parameter and a "helper" trait.
There's a commented line in case 3 that auto-implements InheritTrait on immutable references. Uncommenting this line produces no errors, and (I believe) achieves my ultimate goal of auto-implementing a trait on immutable references. Will this method (using a helper trait that has no methods or type parameters) work to bypass the problem posed by case 2, in general, or are there other subtleties I should worry about?
// ===============================================================================
// TRAIT 1: NO ERROR
// ===============================================================================
// define trait
pub trait GetOneVer1 {
fn get_one(&self) -> usize;
}
// implement on &'a T, where T implements the trait
impl<'a, T: GetOneVer1> GetOneVer1 for &'a T {
fn get_one(&self) -> usize {
1
}
}
// implement on &'a usize
impl<'a> GetOneVer1 for &'a usize {
fn get_one(&self) -> usize {
1
}
}
// ===============================================================================
// TRAIT 2: ERROR
// ===============================================================================
// define trait
pub trait GetOneVer2<Key> {
fn get_one(&self, index: Key) -> usize;
}
// implement on &'a T, where T implements the trait
impl<'a, Key, T: GetOneVer2<Key>> GetOneVer2<Key> for &'a T {
fn get_one(&self, index: Key) -> usize {
1
}
}
// implement on &'a usize
impl<'a, Key> GetOneVer2<Key> for &'a usize {
fn get_one(&self, index: Key) -> usize {
1
}
}
// ===============================================================================
// TRAIT 3: NO ERROR (CORRECTED BY ADDING A SECOND TRAIT REQUIREMENT)
// ===============================================================================
// define "helper trait"
pub trait InheritTrait {}
// impl<'a, T: InheritTrait> InheritTrait for &'a T {} // uncommenting this line does NOT produce any errors
// define trait
pub trait GetOneVer3<Key> {
fn get_one(&self, index: Key) -> usize;
}
// implement on &'a T, where T implements the trait
impl<'a, Key, T: GetOneVer3<Key> + InheritTrait> GetOneVer3<Key> for &'a T {
fn get_one(&self, index: Key) -> usize {
1
}
}
// implement on &'a usize
impl<'a, Key> GetOneVer3<Key> for &'a usize {
fn get_one(&self, index: Key) -> usize {
1
}
}
fn main() {
println!("Hello, world!");
}
Why does case 2 produce errors whereas cases 1 and 3 do not? The only difference I see is that, in case 2, the trait has a generic type parameter, and in case 3 there are both a generic type parameter and a "helper" trait.
The generic parameter is exactly the problem.
Who can implement GetOneVer1 for usize? Only you (who defined the trait) or the owner of usize (std).
Who can implement GetOneVer2<Key> for usize for some Key? You, usize's owner, or anyone who owns Key. And since Key is generic, that means everyone.
The orphan rules have not problems with a potential conflict between the owner of the trait and the owner of the type, because the compiler can see this impl does not exist (it depends only on upstream crates, std in this example). However, in the second case, downstream crates may implement it - and thus the compiler cannot prove this impl will not exist.
In the third case, we re-added the guarantee - as long as no impl InheritTrait for usize exists (which only you and std can provide), no trait can get the blanket implementation for references. Indeed, if you will add this impl, the compiler will error.
There's a commented line in case 3 that auto-implements InheritTrait on immutable references. Uncommenting this line produces no errors, and (I believe) achieves my ultimate goal of auto-implementing a trait on immutable references. Will this method (using a helper trait that has no methods or type parameters) work to bypass the problem posed by case 2, in general, or are there other subtleties I should worry about?
It will not work, because you still need impl InheritTrait for usize, and like we saw, this impl is impossible to write.
Suppose I have some custom collection of Foos:
struct Bar {}
struct Foo {
bar: Bar
}
struct SubList {
contents: Vec<Foo>,
}
and suppose I also have a SuperList which is a custom collection of SubLists:
struct SuperList {
contents: Vec<SubList>,
}
SubList and SuperList each provide a method bars:
impl SubList {
fn bars(&self) -> impl Iterator<Item = &Bar> + '_ {
self.contents.iter().map(|x| &x.bar)
}
}
impl SuperList {
fn bars(&self) -> impl Iterator<Item = &Bar> + '_ {
self.contents.iter().flat_map(|x| x.items())
}
}
I want to define a trait that provides a method items, and implement that trait on SubList and SuperList so that SubList::items is equivalent to SubList::bars and SuperList::items is equivalent to SuperList::bars, so that I can do this:
fn do_it<T: Buz<Bar>>(buz: &T) {
for item in buz.items() {
println!("yay!")
}
}
fn main() {
let foos = vec![Foo{ bar: Bar{} }];
let sublist = SubList{ contents: foos };
do_it(&sublist);
let superlist = SuperList{ contents: vec![sublist] };
do_it(&superlist);
}
I can do what I want with dynamic dispatch:
trait Buz<T> {
fn items(&self) -> Box<dyn Iterator<Item = &T> + '_>;
}
impl Buz<Bar> for SubList {
fn items(&self) -> Box<dyn Iterator<Item = &Bar> + '_> {
SubList::bars(self)
}
}
impl Buz<Bar> for SuperList {
fn items(&self) -> Box<dyn Iterator<Item = &Bar> + '_> {
SuperList::bars(self)
}
}
However, the following doesn't work:
trait Baz<T> {
fn items(&self) -> impl Iterator<Item = &T> + '_;
}
impl Baz<Bar> for SubList {
fn items(&self) -> impl Iterator<Item = &Bar> + '_ {
SubList::bars(self)
}
}
impl Baz<Bar> for SuperList {
fn items(&self) -> impl Iterator<Item = &Bar> + '_ {
SuperList::bars(self)
}
}
(error[E0562]: `impl Trait` not allowed outside of function and inherent method return types)
Here's a playground link to what I've tried so far
How can I define a trait Baz which provides an items method to abstract over the bars methods of SubList and SuperList without using dynamic dispatch?
Unfortunately, what you are trying to do is not really possible in Rust right now. Not by design, but simply because some relevant type level features are not implemented or stabilized yet.
Unless you have spare time, an interest in type level stuff and are willing to use nightly: just use boxed iterators. They are simple, they just work, and in most cases it will likely not even hurt performance in a meaningful way.
You're still reading? Ok let's talk about it.
As you intuitively tried, impl Trait in return type position would be the obvious solution here. But as you noticed, it doesn't work: error[E0562]: `impl Trait` not allowed outside of function and inherent method return types. Why is that? RFC 1522 says:
Initial limitations:
impl Trait may only be written within the return type of a freestanding or inherent-impl function, not in trait definitions [...] Eventually, we will want to allow the feature to be used within traits [...]
These initial limitations were put in place because the type level machinery to make this work was/is not in place yet:
One important usecase of abstract return types is to use them in trait methods.
However, there is an issue with this, namely that in combinations with generic trait methods, they are effectively equivalent to higher kinded types. Which is an issue because Rust's HKT story is not yet figured out, so any "accidental implementation" might cause unintended fallout.
The following explanation in the RFC is also worth reading.
That said, some uses of impl Trait in traits can be achieved already today: with associated types! Consider this:
trait Foo {
type Bar: Clone;
fn bar() -> Self::Bar;
}
struct A;
struct B;
impl Foo for A {
type Bar = u32;
fn bar() -> Self::Bar { 0 }
}
impl Foo for B {
type Bar = String;
fn bar() -> Self::Bar { "hello".into() }
}
This works and is "basically equivalent" to:
trait Foo {
fn bar() -> impl Clone;
}
Each impl block can choose a different return type as long as it implements a trait. So why then does impl Trait not simply desugar to an associated type? Well, let's try with your example:
trait Baz<T> {
type Iter: Iterator<Item = &Bar>;
fn items(&self) -> Self::Iter;
}
We get a missing lifetime specifier error:
4 | type Iter: Iterator<Item = &Bar>;
| ^ expected named lifetime parameter
Trying to add a lifetime parameter... we notice that we can't do that. What we need is to use the lifetime of &self here. But we can't get it at that point (in the associated type definition). This limitation is very unfortunate and is encountered in many situations (search term "streaming iterator"). The solution here are GATs: generic associated types. They allow us to write this:
trait Baz<T> {
// vvvv That's what GATs allow
type Iter<'s>: Iterator<Item = &'s Bar>;
fn items(&self) -> Self::Iter<'_>;
}
GATs are not fully implemented and certainly not stable yet. See the tracking issue.
But even with GATs, we cannot fully make your example work. That's because you use iterator types that are unnameable, due to using closures. So the impl Baz for ... blocks would not be able to provide the type Iter<'s> definition. Here we can use another feature that is not stable yet: impl Trait in type aliases!
impl Baz<Bar> for SubList {
type Iter<'s> = impl Iterator<Item = &'s Bar>;
fn items(&self) -> Self::Iter<'_> {
SubList::bars(self)
}
}
This actually works! (Again, on nightly, with these unstable features.) You can see your full, working example in this playground.
This type system work has been going on for a long time and it seems like it's slowly reaching a state of being usable. (Which I am very happy about ^_^). I expect that a few of the foundational features, or at least subsets of them, will be stabilized in the not-too-distant future. And once these are used in practice and we see how they work, more convenience features (like impl Trait in traits) will be reconsidered and stabilized. Or so I think.
Also worth noting that async fns in traits are also blocked by this, since they basically desugar into a method returning impl Future<Output = ...>. And those are also a highly requested feature.
In summary: these limitations have been a pain point for quite some time and they resurface in different practical situations (async, streaming iterator, your example, ...). I'm not involved in compiler development or language team discussions, but I kept an eye on this topic for a long time and I think we are getting close to finally resolving a lot of these issues. Certainly not in the next releases, but I see a decent chance we get some of this in the next year or two.
Context: I am working with an external crate that defines a trait ExternalTrait
eg
pub trait ExternalTrait {
fn associated_func() -> i32;
}
I have several types that have almost identical implementations of said trait, differing only by a single value, so I want to defined my own trait implementing the external one where the only required definition is the value that differs.
e.g.
trait MyImplementation {
fn custom_value() -> i32;
}
One way i attempted to do this that does not work is
impl ExternalTrait for dyn MyImplementation {
fn associated_func() -> i32 {
&Self::custom_value()
}
}
which fails because MyImplementation, having an associated function, is not object safe.
What does work is
impl<T> ExternalTrait for T
where
T: MyImplementation,
{
fn associated_func() -> i32 {
T::custom_value()
}
}
which is great, except that the external crate I'm working with already has a generic implementation for that trait, like this:
impl<'a, Y> ExternalTrait for &'a Y
where
Y: ?Sized,
Y: ExternalTrait,
{
fn associated_func() -> i32 {
10
}
}
which understandably conflicts with the one I'm defining. So the question is, is there a way to support my use-case, aside from using macros?
Cheers
e: additional context. The above was just me trying to come up with a minimal example. The actual external trait that I'm running into this in practice is https://docs.rs/juniper/0.15.1/juniper/trait.GraphQLType.html and conflicting implementation is https://docs.rs/juniper/0.15.1/juniper/trait.GraphQLType.html#impl-GraphQLType%3CS%3E-for-%26%27e%20T. It is possible to implement it for a concrete struct just fine, e.g.
impl GraphQLType<DefaultScalarValue> for MyStruct
I have this trait and simple structure:
use std::path::{Path, PathBuf};
trait Foo {
type Item: AsRef<Path>;
type Iter: Iterator<Item = Self::Item>;
fn get(&self) -> Self::Iter;
}
struct Bar {
v: Vec<PathBuf>,
}
I would like to implement the Foo trait for Bar:
impl Foo for Bar {
type Item = PathBuf;
type Iter = std::slice::Iter<PathBuf>;
fn get(&self) -> Self::Iter {
self.v.iter()
}
}
However I'm getting this error:
error[E0106]: missing lifetime specifier
--> src/main.rs:16:17
|
16 | type Iter = std::slice::Iter<PathBuf>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected lifetime parameter
I found no way to specify lifetimes inside that associated type. In particular I want to express that the iterator cannot outlive the self lifetime.
How do I have to modify the Foo trait, or the Bar trait implementation, to make this work?
Rust playground
There are a two solutions to your problem. Let's start with the simplest one:
Add a lifetime to your trait
trait Foo<'a> {
type Item: AsRef<Path>;
type Iter: Iterator<Item = Self::Item>;
fn get(&'a self) -> Self::Iter;
}
This requires you to annotate the lifetime everywhere you use the trait. When you implement the trait, you need to do a generic implementation:
impl<'a> Foo<'a> for Bar {
type Item = &'a PathBuf;
type Iter = std::slice::Iter<'a, PathBuf>;
fn get(&'a self) -> Self::Iter {
self.v.iter()
}
}
When you require the trait for a generic argument, you also need to make sure that any references to your trait object have the same lifetime:
fn fooget<'a, T: Foo<'a>>(foo: &'a T) {}
Implement the trait for a reference to your type
Instead of implementing the trait for your type, implement it for a reference to your type. The trait never needs to know anything about lifetimes this way.
The trait function then must take its argument by value. In your case you will implement the trait for a reference:
trait Foo {
type Item: AsRef<Path>;
type Iter: Iterator<Item = Self::Item>;
fn get(self) -> Self::Iter;
}
impl<'a> Foo for &'a Bar {
type Item = &'a PathBuf;
type Iter = std::slice::Iter<'a, PathBuf>;
fn get(self) -> Self::Iter {
self.v.iter()
}
}
Your fooget function now simply becomes
fn fooget<T: Foo>(foo: T) {}
The problem with this is that the fooget function doesn't know T is in reality a &Bar. When you call the get function, you are actually moving out of the foo variable. You don't move out of the object, you just move the reference. If your fooget function tries to call get twice, the function won't compile.
If you want your fooget function to only accept arguments where the Foo trait is implemented for references, you need to explicitly state this bound:
fn fooget_twice<'a, T>(foo: &'a T)
where
&'a T: Foo,
{}
The where clause makes sure that you only call this function for references where Foo was implemented for the reference instead of the type. It may also be implemented for both.
Technically, the compiler could automatically infer the lifetime in fooget_twice so you could write it as
fn fooget_twice<T>(foo: &T)
where
&T: Foo,
{}
but it's not smart enough yet.
For more complicated cases, you can use a Rust feature which is not yet implemented: Generic Associated Types (GATs). Work for that is being tracked in issue 44265.
Use a wrapper type
If the trait and all its implementations are defined in one crate, a helper type can be useful:
trait Foo {
fn get<'a>(&'a self) -> IterableFoo<'a, Self> {
IterableFoo(self)
}
}
struct IterableFoo<'a, T: ?Sized + Foo>(pub &'a T);
For a concrete type that implements Foo, implement the iterator conversion on the IterableFoo wrapping it:
impl Foo for Bar {}
impl<'a> IntoIterator for IterableFoo<'a, Bar> {
type Item = &'a PathBuf;
type IntoIter = std::slice::Iter<'a, PathBuf>;
fn into_iter(self) -> Self::IntoIter {
self.0.v.iter()
}
}
This solution does not allow implementations in a different crate. Another disadvantage is that an IntoIterator bound cannot be encoded into the definition of the trait, so it will need to be specified as an additional (and higher-rank) bound for generic code that wants to iterate over the result of Foo::get:
fn use_foo_get<T>(foo: &T)
where
T: Foo,
for<'a> IterableFoo<'a, T>: IntoIterator,
for<'a> <IterableFoo<'a, T> as IntoIterator>::Item: AsRef<Path>
{
for p in foo.get() {
println!("{}", p.as_ref().to_string_lossy());
}
}
Associated type for an internal object providing desired functionality
The trait can define an associated type that gives access to a part of the object that, bound in a reference, provides the necessary access traits.
trait Foo {
type Iterable: ?Sized;
fn get(&self) -> &Self::Iterable;
}
This requires that any implementation type contains a part that can be so exposed:
impl Foo for Bar {
type Iterable = [PathBuf];
fn get(&self) -> &Self::Iterable {
&self.v
}
}
Put bounds on the reference to the associated type in generic code that uses the the result of get:
fn use_foo_get<'a, T>(foo: &'a T)
where
T: Foo,
&'a T::Iterable: IntoIterator,
<&'a T::Iterable as IntoIterator>::Item: AsRef<Path>
{
for p in foo.get() {
println!("{}", p.as_ref().to_string_lossy());
}
}
This solution permits implementations outside of the trait definition crate.
The bound work at generic use sites is as annoying as with the previous solution.
An implementing type may need an internal shell struct with the only purpose of providing the associated type, in case when the use-site bounds are not as readily satisfied as with Vec and IntoIterator in the example discussed.
In future, you'll want an associated type constructor for your lifetime 'a but Rust does not support that yet. See RFC 1598