Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Working around the limitations of extension traits

The pattern of having an object-safe trait Foo and a (potentially unsafe) extension trait FooExt implemented for all instances of Foo seems to become standard now.

https://github.com/rust-lang/rfcs/pull/445

This is a problem for me in the case of Iterator<A>, as I have a library that overrides the default method IteratorExt#last() of the old iterator trait (the underlying library has an efficient implementation of last()). This in now impossible, because for any A, there will always be a conflicting trait implementation of IteratorExt, the one that libcore already provides for all Iterator<A>.

iterator.rs:301:1: 306:2 error: conflicting implementations for trait `core::iter::IteratorExt` [E0119]

iterator.rs:301 impl<'a, K: Key> iter::IteratorExt<Vec<u8>> for ValueIterator<'a,K,Vec<u8>> {
iterator.rs:302   fn last(&mut self) -> Option<Vec<u8>> {
iterator.rs:303      self.seek_last();
iterator.rs:304      Some(self.value())
iterator.rs:305   }
iterator.rs:306 }
...

Now, as far as I see, I have two options:

  • have my own trait and my own last() implementation. That would mean it conflicts if IteratorExt is imported unless carefully used. This also has the danger accidentally using an inefficient version of last() if the version from IteratorExt is used. I'd loose convenient access to IteratorExt.
  • have my own trait and name the method differently (seek_last()). Disadvantage: I ask the user to learn vocabulary and to always favor my method over that provided by IteratorExt. Same problem: I'd like to avoid accidental usage of last().

Is there any other, better, solution I am missing?

like image 698
Skade Avatar asked Dec 03 '14 09:12

Skade


1 Answers

As of rustc 0.13.0-nightly (8bca470c5 2014-12-08 00:12:30 +0000) defining last() as an inherent method on your type should work.

#[deriving(Copy)]
struct Foo<T> {t: T}

impl<T> Iterator<T> for Foo<T> {
    fn next(&mut self) -> Option<T> { None }
}

// this does not work
// error: conflicting implementations for trait `core::iter::IteratorExt` [E0119]
// impl<T> IteratorExt<T> for Foo<T> {
//    fn last(mut self) -> Option<T> { None }
//}

// but this currently does
impl<T> Foo<T> {
    fn last(mut self) -> Option<T> { Some(self.t) }
}


fn main() {
    let mut t = Foo{ t: 3u };
    println!("{}", t.next())
    println!("{}", t.last()) // last has been "shadowed" by our impl
    println!("{}", t.nth(3)) // other IteratorExt methods are still available
}

Since you're not supposed to use Extension traits as generic bounds (but just to provide additional methods), this should theoretically work for your scenario, as you can have your own type and its impl in the same crate.

Users of your type will use the inherent last method instead of the one on IteratorExt but still be able to use the other methods on IteratorExt.

like image 53
Paolo Falabella Avatar answered Nov 11 '22 08:11

Paolo Falabella