alex elias
alex elias

Reputation: 341

Immutable access in rust

I am new to rust from python and have used the functional style in python extensively.

What I am trying to do is to take in a string (slice) (or any iterable) and iterate with a reference to the current index and the next index. Here is my attempt:

fn main() {
    // intentionally immutable, this should not change
    let x = "this is a
multiline string
with more
then 3 lines.";

    // initialize multiple (mutable) iterators over the slice
    let mut lineiter = x.chars();
    let mut afteriter = x.chars();
    // to have some reason to do this
    afteriter.skip(1);

    // zip them together, comparing the current line with the next line
    let mut zipped = lineiter.zip(afteriter);

    for (char1, char2) in zipped {
        println!("{:?} {:?}", char1, char2);
    }
}

I think it should be possible to get different iterators that have different positions in the slice but are referring to the same parts of memory without having to copy the string, but the error I get is as follows:

error[E0382]: use of moved value: `afteriter`
  --> /home/alex/Documents/projects/simple-game-solver/src/src.rs:15:35
   |
10 |     let afteriter = x.chars();
   |         --------- move occurs because `afteriter` has type `std::str::Chars<'_>`, which does not implement the `Copy` trait
11 |     // to have some reason to do this
12 |     afteriter.skip(1);
   |     --------- value moved here
...
15 |     let mut zipped = lineiter.zip(afteriter);
   |                                   ^^^^^^^^^ value used here after move

I also get a warning telling me that zipped does not need to be mutable.

Is it possible to instantiate multiple iterators over a single variable and if so how can it be done?

Upvotes: 1

Views: 303

Answers (2)

Masklinn
Masklinn

Reputation: 42492

Is it possible to instantiate multiple iterators over a single variable and if so how can it be done?

If you check the signature and documentation for Iterator::skip:

fn skip(self, n: usize) -> Skip<Self>

Creates an iterator that skips the first n elements.

After they have been consumed, the rest of the elements are yielded. Rather than overriding this method directly, instead override the nth method.

You can see that it takes self by value (consumes the input iterator) and returns a new iterator. This is not a method which consumes the first n elements of the iterator in-place, it's one which converts the existing iterator into one which skips the first n elements.

So instead of:

let mut afteriter = x.chars();
afteriter.skip(1);

you just write:

let mut afteriter = x.chars().skip(1);

I also get a warning telling me that zipped does not need to be mutable.

That's because Rust for loop uses the IntoIterator trait, which moves the iterable into the loop. It's not creating a mutable reference, it's just consuming whatever the RHS is.

Therefore it doesn't care what the mutability of the variable. You do need mut if you iterate explicitly, or if you call some other "terminal" method (e.g. nth or try_fold or all), or if you want to iterate on the mutable reference (that's mostly useful for collections though), but not to hand off iterators to some other combinator method, or to a for loop.

A for loop takes self, if you will. Just as for_each does in fact.

Upvotes: 4

alex elias
alex elias

Reputation: 341

Thanks to @Stargateur for giving me the solution. The .skip(1) takes ownership of afteriter and returns ownership to a version without the first element. What was happening before was ownership was lost on the .skip and so the variable could not be mutated anymore (I am pretty sure)

Upvotes: 0

Related Questions