1

I am trying to write a conversion trait that can take both Iterators to references and Iterators to mutable references.

Sadly, it seems that once you implement a trait for Iter<&>, you cannot implement the trait also for Iter<&mut> as they seem to collide. Which doesn't make sense, though, because Iter<&mut> doesn't seem to be able to utilize the implementation for Iter<&>.

Here is a minimal example:

// A dummy object
#[derive(Debug)]
struct MyObj(i32);

// A collection of MyObj references
#[derive(Debug)]
struct RefVec<'a>(Vec<&'a MyObj>);

// Goal: create a conversion function that can take *either* a `Iterator<&MyObj>` *or* `Iterator<&mut MyObj>`.
// Attempt: Create a Conversion Trait and implement it for both of those types
trait ConvertToRefVec<'a> {
    fn convert(&mut self) -> RefVec<'a>;
}

// Implement the conversion for Iter<&MyObj>
impl<'a, T> ConvertToRefVec<'a> for T
where
    T: Iterator<Item = &'a MyObj>,
{
    fn convert(&mut self) -> RefVec<'a> {
        RefVec(self.collect::<Vec<_>>())
    }
}

// Problem: the impl above does not apply to Iter<&mut MyObj>. But the attempt to write an impl
// for Iter<&mut MyObj> fails with the message that it collides with the impl above:
impl<'a, T> ConvertToRefVec<'a> for T
where
    T: Iterator<Item = &'a mut MyObj>,
{
    fn convert(&mut self) -> RefVec<'a> {
        RefVec(self.collect::<Vec<_>>())
    }
}

fn main() {
    let owned = [MyObj(42), MyObj(69)];

    let ref_vec = owned.iter().convert();
    println!("{:?}", ref_vec);

    let ref_vec = owned.iter_mut().convert();
    println!("{:?}", ref_vec);
}

Which gives me:

error[E0119]: conflicting implementations of trait `ConvertToRefVec<'_>`
  --> src/main.rs:27:1
   |
16 | / impl<'a, T> ConvertToRefVec<'a> for T
17 | | where
18 | |     T: Iterator<Item = &'a MyObj>,
19 | | {
...  |
22 | |     }
23 | | }
   | |_- first implementation here
...
27 | / impl<'a, T> ConvertToRefVec<'a> for T
28 | | where
29 | |     T: Iterator<Item = &'a mut MyObj>,
30 | | {
...  |
33 | |     }
34 | | }
   | |_^ conflicting implementation

But if I leave out the impl for Iterator<&mut>, then I get this error:

error[E0599]: the method `convert` exists for struct `std::slice::IterMut<'_, MyObj>`, but its trait bounds were not satisfied
   --> src/main.rs:42:36
    |
42  |     let ref_vec = owned.iter_mut().convert();
    |                                    ^^^^^^^ method cannot be called on `std::slice::IterMut<'_, MyObj>` due to unsatisfied trait bounds
    |
   ::: /home/.../.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/slice/iter.rs:187:1
    |
187 | pub struct IterMut<'a, T: 'a> {
    | -----------------------------
    | |
    | doesn't satisfy `<_ as Iterator>::Item = &MyObj`
    | doesn't satisfy `std::slice::IterMut<'_, MyObj>: ConvertToRefVec`
    |
note: trait bound `<std::slice::IterMut<'_, MyObj> as Iterator>::Item = &MyObj` was not satisfied
   --> src/main.rs:18:17
    |
16  | impl<'a, T> ConvertToRefVec<'a> for T
    |             -------------------     -
17  | where
18  |     T: Iterator<Item = &'a MyObj>,
    |                 ^^^^^^^^^^^^^^^^ unsatisfied trait bound introduced here

So my question is: How can I write such a conversion mechanism that works with both Iterator<Item = &MyObj> and Iterator<Item = &mut MyObj?

Finomnis
  • 18,094
  • 1
  • 20
  • 27
  • First and foremost, `impl` block for `&mut T` is not correct per se. Try to comment the first implementation and println and compile. There will be discordance in mutability (`RefVec` keeps a `Vec` of **immutable** references, but you are trying to keep them *mutable*). The this is the following: the trait `FromIterator<&'a mut MyObj>` is not implemented for `Vec<&T>` – unegare Jun 07 '22 at 10:39
  • @unegare No, I'm not trying to keep them mutable, I will very much store them as immutable references. I just want to be able to receive an iterator of mutable references, because mutable references can without a problem be coerced into immutable references. – Finomnis Jun 07 '22 at 12:04
  • Thanks to whoever suggested the duplicate question, its answer hit the problem I had on the head. – Finomnis Jun 07 '22 at 12:41

1 Answers1

0

The thing is that you are trying to implement your trait in two different ways for the same type. So the problem that you treat T as the same one. Then you can just split the cases for concrete types. Like to following:

#[derive(Debug)]
struct MyObj(i32);

#[derive(Debug)]
struct RefVec<'a>(Vec<&'a MyObj>);

trait ConvertToRefVec<'a> {
    fn convert(&mut self) -> RefVec<'a>;
}

impl<'a> ConvertToRefVec<'a> for std::slice::Iter<'a, MyObj> 
{
    fn convert(&mut self) -> RefVec<'a> {
        RefVec(self.collect::<Vec<_>>())
    }
}

impl<'a> ConvertToRefVec<'a> for std::slice::IterMut<'a, MyObj>
{
    fn convert(&mut self) -> RefVec<'a> {
        RefVec(self.map(|el| &*el).collect::<Vec<&MyObj>>())
    }
}

fn main() {
    let mut owned = [MyObj(42), MyObj(69)];

    let ref_vec = owned.iter().convert();
    println!("{:?}", ref_vec);

    let ref_vec = owned.iter_mut().convert();
    println!("{:?}", ref_vec);
}
unegare
  • 2,197
  • 1
  • 11
  • 25
  • But that would assume that the iterator is a slice iterator, right? Is this impossible for iterators in general? – Finomnis Jun 07 '22 at 12:02
  • "trying to implement your trait in two different ways for the same type" - if you are saying I need to implement the type only a single time, then how do I tell it that I want to accept `Iterator` with either `&mut` or `&`? To me it seems like `Iterator<&mut>` is a different 'sub-trait' then `Iterator<&>`, although I'm sure that concept doesn't exist – Finomnis Jun 07 '22 at 12:08
  • @Finomnis, It is inextricably linked with the absence of *negative trait bounds* in Rust. And it cannot be helped. They call it 'one of the greatest limitation of the language and a recurring pain point.' https://github.com/rust-lang/rust/issues/42721 – unegare Jun 07 '22 at 13:30
  • I'm not sure if your assessment fits my question. The link of the duplicate question helped me implement this without negative trait bounds: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=f9b59ca9309bff3fe996b05baaab8c13 – Finomnis Jun 07 '22 at 14:01