Maybe an iterator that returns mutable reference is possible in this case?

I get why iterators that return mutable references are not possible in Rust without unsafe code, and I don't want to use unsafe code even if it looks safe. But I believe that for this particular case of mine, maybe there's a way, because I never need 2 mutable references, or one mutable + other const reference, at the same time. I just want to add 2 polynomials coefficient-wise:

pub struct Coefficients<'a, T> {
    _phantom: &'a T,
}

impl<'a, T> Iterator for Coefficients<'a, T> {
    type Item = &'a T;
    
    #[inline(always)]
    fn next(&mut self) -> Option<&'a T> {
        unimplemented!()
    }
}

pub fn coefficient_wise_add<'a, T>(
    operand1: Coefficients<'a, T>,
    operand2: Coefficients<'a, T>,
    destination: Coefficients<'a, T>,
) {
    for _tuple in operand1.zip(operand2).zip(destination) {
        //can't set destination here, but wanted to do 
        //*tuple.2 = *tuple.0 + *tuple.1;
    }
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=53a3e68aaecdcb90056d94644f59fa22

Is there a way to force the compiler to understand that I never use 2+ mutable references or 1 mutable + many const?

The short answer would be, mostly not.
A pretty good explanation is in the Nomicon.

1 Like

It sounds like you just need an iter_mut for destination? I assume the destination does not overlap with the two input operands?

2 Likes

This assumption is not quite right. Iterators that return items of type &mut SomeType are often harder to implement (compared to iterators with &SomeType items), but also often still possible without unsafe code.

I’m not sure I understand your example code though, because that’s an iterator that does not return mutable references.

3 Likes

yes they don't overlap. But I just tested and this works:

pub struct Coefficients<'a, T> {
    _phantom: &'a T,
}

impl<'a, T> Iterator for Coefficients<'a, T> {
    type Item = &'a mut T;
    
    #[inline(always)]
    fn next(&mut self) -> Option<&'a mut T> {
        unimplemented!()
    }
}

pub fn coefficient_wise_add<'a, T: std::ops::Add<Output = T> + Clone>(
    operand1: Coefficients<'a, T>,
    operand2: Coefficients<'a, T>,
    destination: Coefficients<'a, T>,
) {
    for tuple in operand1.zip(operand2).zip(destination) {
        *tuple.1 = tuple.0.0.clone() + tuple.0.1.clone();
    }
}

I guess the problem is not returning &mut then, it's how I use it?

However cloning here seems a bit too much, unless it's inlined. Calling a clone function would be too much for performance I guess.

I don't really understand the problem you've run into.

Regarding the clone, that's a completely separate question, and how expensive it is depends on the underlying type. If it's an integer such as i32, it would not be expensive.

pub struct Coefficients<'a, T> {
    a: &'a mut T,
}

impl<'a, T> Iterator for Coefficients<'a, T> {
    type Item = &'a mut T;
    
    #[inline(always)]
    fn next(&'a mut self) -> Option<&'a mut T> {
        Some(self.a)
    }
}

pub fn coefficient_wise_add<'a, T: std::ops::Add<Output = T> + Clone>(
    operand1: Coefficients<'a, T>,
    operand2: Coefficients<'a, T>,
    destination: Coefficients<'a, T>,
) {
    for tuple in operand1.zip(operand2).zip(destination) {
        *tuple.1 = tuple.0.0.clone() + tuple.0.1.clone();
    }
}

When I tried to actually implement the next, I got:

error[E0308]: method not compatible with trait
 --> src/lib.rs:9:5
  |
9 |     fn next(&'a mut self) -> Option<&'a mut T> {
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
  |
  = note: expected fn pointer `fn(&mut Coefficients<'a, T>) -> Option<&mut T>`
             found fn pointer `fn(&'a mut Coefficients<'a, T>) -> Option<&'a mut T>`
note: the anonymous lifetime #1 defined on the method body at 9:5...
 --> src/lib.rs:9:5
  |
9 |     fn next(&'a mut self) -> Option<&'a mut T> {
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...does not necessarily outlive the lifetime `'a` as defined on the impl at 5:6
 --> src/lib.rs:5:6
  |
5 | impl<'a, T> Iterator for Coefficients<'a, T> {
  |      ^^

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=bdbacf1f80ddfc55fed5901e39425ce9

If your code was allowed conceptually, I could get two mutable references to the same data by calling next() twice(because you always return the same mutable reference). You need some static guarantee that this cannot happen.

Moreover, your a in Coefficients is "trapped" behind your mutable borrow in Iterator::next. You can't change the signature of next to work around the lifetime. That is, you must implement with fn next(&mut self), not fn next(&'a mut self).

You should look into having a dedicated data structure for this one-time iteration case, just like [T] has dedicated structures for mutable iteration and immutable iteration, and how Vec has a dedicated structure for consuming iteration.

If you don't want to go to all that trouble, you could just do something like this:

pub type CoefficientsIter<'a, T> = Option<&'a mut T>;

impl<'a, T> Coefficients<'a, T> {
    fn iter(&mut self) -> CoefficientsIter<'_, T> {
        // Reborrow for a (probably) shorter lifetime
        Some(&mut self.a)
    }
}

But really, while spelling it out more explicitly is a bit more boiler-plate, it's not too bad.

1 Like

For returning an actual iterator, using option::IntoIter<&'a mut T> for type CoefficientsIter<'a, T> is an option as-well. Or array::IntoIter<&'a mut T, 1>. Created with Some(&mut self.a).into_iter() or [&mut self.1].into_iter(), respectively.

2 Likes
for element in slice.iter_mut() {
    *element += 1;
}

how does this work? I don't see IterMut implementing Iterator

Here it is.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.