Custom generic trait over a generic Slice - unable to get it working

Cannot get the following code to compile after multiple attempted variations. All I am trying to do is get a Generic trait to work for a Slice which, in turn, is itself generic.

Playground for the code below here.

#![feature(associated_type_defaults)]

use std::ops::{Mul, AddAssign};

trait Foo<'a, T: 'a> {
    type Item = &'a [T];

    fn assert_equal(&self, other: Self::Item) {
        assert_eq!(self.len(), other.len());
    }
}

impl<'a, T> Foo<'a, T> for &'a [T]
where T: Mul<T, Output=T> + AddAssign {}

pub fn main() {
    let foo = &[1, 2, 3][..];
    println!("{:?}", foo.assert_equal(&[3, 4, 5]));
}

Getting error about len() not available on &Self:

9 |         assert_eq!(self.len(), other.len());
  |                         ^^^ method not found in `&Self`
  = help: items from traits can only be used if the type parameter is bounded by the trait
help: the following trait defines an item `len`, perhaps you need to restrict type parameter `Self` with it:

And, going by compiler suggestion, If I make the trait to be bounded (which I am not sure is the correct way) with the following change:

trait Foo<'a, T: 'a> where Self: Iterator {
    // Rest of code being same 
}

I get another error:

13 | impl<'a, T> Foo<'a, T> for &'a [T]
   |             ^^^^^^^^^^ `T` is not an iterator

And now if I change where on impl to include + Iterator, I am back to the original error.

Any help or guidance appreciated!

  1. There are no immutable iterators. &iter can't be iterated, because iterators must have ability to mutate themselves to advance to the next element.

  2. Collections/slices aren't iterators. They can't iterate themselves. There's always another type that iterates them. In this case https://doc.rust-lang.org/std/slice/struct.Iter.html

  3. You can use IntoIterator trait to handle all potentially-iterable types like the for loop does.

2 Likes

Thanks for the helpful explanation!

I think it is not clear from the code I posted, but I am not specifically looking implement iterators; I experimented with them as trait bounds because of compiler suggestion.

What I am looking to implement is a trait for a generic slice (similar to a question here).

The problem, it seems, is slice itself cannot be used as a bound and the situation, probably, needs some work around.

See, I am able to get the following to work:

use std::ops::{Mul, AddAssign};

trait Foo<T> {
    type Item = [T];

    fn assert_equal(&self, other: &[T]);
}

impl<T> Foo<T> for [T]
where T: Mul<T, Output=T> + AddAssign {
    fn assert_equal(&self, other: &[T]) {
        assert_eq!(self.len(), other.len());
    }
}

pub fn main() {
    let foo = &[1, 2, 3][..];
    foo.assert_equal(&[3, 4, 5]);
}

But if I provide the default implementation for fn assert_equal(&self, other: &[T]) within the trait itself, it fails because the trait cannot "see" the method len() on self but it can when implemented on [T].

I think instructing the compiler (via some bounds) that the trait will always be implemented for types having .len() defined (specifically slice in my case) could work. But I do not know how. :confused:

BTW, iterators already implement comparison: [1].iter().eq([2].iter())


trait Foo<U> {
    fn assert_equal<S>(&self, other: S) where S: AsRef<[U]>;
}

impl<T, U> Foo<U> for T where T: AsRef<[U]>, U: PartialEq + std::fmt::Debug {
    fn assert_equal<S>(&self, other: S) where S: AsRef<[U]> {
        assert_eq!(self.as_ref(), other.as_ref());
    }
}