Hello,
Consider the following code.
#![allow(unused)]
use std::str::Chars;
use std::iter::Iterator;
struct CharIterator<'a> {
char_iterator: Chars<'a>
}
impl<'a> CharIterator<'a> {
fn new(char_iterator: Chars<'a>) -> Self {
Self {
char_iterator: char_iterator
}
}
}
impl Iterator for CharIterator<'_> {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
self.char_iterator.next()
}
}
fn main() {
let text = String::from("This is a text to parse. ");
let mut iterator = CharIterator::new(text.chars());
for _ in &mut iterator {
for _ in &mut iterator {
}
}
}
Obviously, rustc complain about the fact this code tries to create a mutable reference while another is already in scope. But, this here is totally safe, so my question is : how can make this code working, with the simplicity of the for loop ?
this is probably an xy-problem.
what are you trying to do? this code makes no sense.
if you want the cartesian product, clone the iterator:
no it is not. why it is not safe is because of how for loops are generated, as they store the iterator (and thus here the &mut) in a local between loop iterations.
if you want to make it work as you have written it, you can do something like the following
while let Some(_) = (&mut iterator).next() {
while let Some(_) = (&mut iterator).next() {
}
}
which is safe, but almost always incorrect
From my understanding, for loops are just loops that call next on something that implements Iterator, so you don't have to call next yourself, so when the inner loop terminates, it drops its mutable reference and we get back to the first loop : since underscores were put at the place of the variables we don't have unsafety.
Tell me if it's incorrect but I see these loops like just two loops that at each iteration call next on the iterator, and if next is None, terminate, whether it's the inner or the outer loop.
the issue is the other way around.
that is correct, but what this implies is it needs to terminate to "drop" its mutable reference.
as long as the first loop is not terminated, its mutable reference to the iterator is valid, so iterator cannot be used in any way.
A for loop is supposed to loop over every element in an iterator without skipping. The way you wrote the code, the inner loop would consume the iterator, meaning the outer loop would not be able to access those elements because they have already been consumed. Hence the compiler correctly rejects the code. It's not clear what you're actually trying to achieve.
What do you mean by "terminate to drop" ?
here is what you imagine for loops should do (and is safe)
while let Some(_) = (&mut iterator).next() {
while let Some(_) = (&mut iterator).next() {
}
}
here for loops actually do here (and doesn't compile because if it did it would be unsafe)
let mut iter_out = &mut iterator;
while let Some(_) = iter_out.next() {
let mut iter_in = &mut iterator; // not possible because iter_out is live
while let Some(_) = iter_in.next() {
}
}
// iter out lasts until the end of the loop
Yes it's true. But iterators aren't lists, they provide a way to iterates. This way is provided by the next method, so there aren't aware of a skip.
As you can tell by the code failing to compile, the Rust compiler is actually aware of this, and hence it correctly points out the error. This is actually the main selling point of Rust: it can protect you from such mistakes in a way that a C compiler can't.
In your code the outer loop takes a unique reference to the iterator, which guarantees nobody else is allowed to mess with the iterator during the loop. The inner loop is trying to mess with it, hence the error.
while loops used this way are exactly what I want. Your for loops example explains why Rust considers the first code I sent unsafe, it's exactly what the compiler does with for loops ? And in order to know Rust more precisely, what's inside the condition of while loops is scoped, is that correct ?
See here for something more precise.
It's rather that the compiler prevents the creation of a new mutable reference when another lives, not that it prevents iterators to be skipped.
It's both. A for loop does not allow modifying the iterator in the loop body. It's by design: a for loop expresses simple iteration over consecutive elements in an iterator. Modifying the iterator would interfere with this concept.
The mechanism it uses to have such control is owning the iterator and not exposing access to it in the loop body.
Here the iterator is a mutable reference to another iterator, but it's the same idea: owning a mutable reference to a resource gives you exclusive control over that resource.
If you need a more complicated looping scheme with manual control over the iterator, you need to use loop or while rather than for.
Is there a way to close a topic or it's only automatic in Discourse ?
They're usually just left open until they close from inactivity, but if you want yours closed immediately for some reason, ask and/or flag the topic for moderator attention and someone will get to it.
I would change the category to Help (edit the title to do this) and then mark one of the replies as the solution. This marks it as solved.