Writing to a `[u8]` slice while an iterator is iterating

I have an iterator that reads a [u8] slice one value at a time. Every time it returns Some it moves forward in the slice. It never goes backwards or reads the same value twice.

At the same time as using this iterator, I want to be able to write to a value as soon as it has done reading that value. I'm not sure how to do this safely. Any ideas?

A silly example:

// Removes vowels in place using an iterator. Returns the new sub-slice.
// Can I do this safely?
fn remove_vowels_unsafe(ascii: &mut [u8]) -> &mut [u8] {
    let mut length = 0;
    // unsafe?
    unsafe {
        let ptr = ascii.as_mut_ptr(); // subvert Rust's rules
        for chr in no_vowels_filter(ascii.iter().copied()).take(ascii.len()) {
            *ptr.add(length) = chr;
            length += 1;
        }
    }
    &mut ascii[0..length]
}

Playground link.

I believe that what you are doing is actually sound, but this relies on an implementation detail of slice::iter, namely that the slice iterator is implemented using raw pointers, and that you only modify values after the iterator has read them.

However, in your case you can just use safe code:

use std::cell::Cell;

// Removes vowels in place and returns the new sub-slice.
fn remove_vowels_unsafe(ascii: &mut [u8]) -> &mut [u8] {
    let mut length = 0;
    
    {
        let ascii = Cell::from_mut(ascii).as_slice_of_cells();
        for chr in no_vowels_filter(ascii.iter().map(|i| i.get())).take(ascii.len()) {
            ascii[length].set(chr);
            length += 1;
        }
    }
    
    &mut ascii[0..length]
}

playground

4 Likes

Ah thanks! I must learn more about using Cell.

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.