Aliased data structure, what to do about them?

I am a long time Java programmer, and I really did not need to avoid aliasing in that language (memory bugs not withstanding). Now that I am practicing Rust programming, of course I try to minimize aliasing. However, I am finding sometimes it is not possible to reduce the amount of aliasing to zero.

Right now I just have one example that most iterators will scan over your data structure that has a collection, and form an alias during the traversal. Do Rust programmers admit that you do not have access to your controlling data structure 100% of the time?

As a library writer I could say that my library will not support iterators (unattractive), but I think there are other times that there will be other sources of unavoidable aliasing, despite best effort to avoid them.

I might be inclined to cut features that requires control data structure access during iterator traversal; that is a valid trade-off there...

If you're used to the Java, you can think this alias feature as a way to make ConcurrentModificationException into compile error. Collections in Java do some extra works to detect/throw said exception. In Rust all such checks are done in compile time. No exceptions, no cost, no Saturday night oncalls for bugs that can never be reproduced during work hours.

Could you elaborate this more? I'm not sure what the question is.

1 Like
// mutable states here
pub struct ConverterState {
    my_buf: [u8; 2],
    my_invalid_sequence: bool,
}

impl ConverterState {

    pub fn new() -> ConverterState {
        ConverterState {
            my_buf: [0u8; 2],
            my_invalid_sequence: false,
        }
    }

    pub fn sum(& mut self) -> u32 {
        let res = (self.my_buf[0] as u32) + (self.my_buf[1] as u32);
        if res >= 256 {
            self.signal_invalid_sequence();
        }
        res
    }

    pub fn signal_invalid_sequence(& mut self) {
        self.my_invalid_sequence = true;
    }

    pub fn has_invalid_sequence(&self) -> bool {
        self.my_invalid_sequence
    }
}

I am working on an UTF8/UTF32 conversion functionality. My converter state has a small buffer to save up incomplete bytes that are not yet a completed UTF8 sequence. There are also error states to help indicate conversion error. Now an user of my library can do:

/// Single buffer iterator based UTF8 parsing
/// converting to char
fn main() {
    let mybuffer = "abc\n".as_bytes();
    let mut utf8_ref_iter = mybuffer.iter();
    let mut parser = FromUtf8::new();
    let iterator = parser.utf8_ref_to_char_with_iter(& mut utf8_ref_iter);
    for char_val in iterator {
        print!("{}", char_val);
    }
}

In the "for loop" the iterator will form a mut borrow to struct ConverterState, so the client caller cannot access the parser object "FromUtf8", nor methods such as has_invalid_sequence(). That is the restrictions I am talking about.

That's a weird way of framing unique borrowing. It's like asking, "Do Rust programmers admit that you can't compile 100% of the code you can write?" - and of course, that's the very point of a strong type system, it prevents (most) incorrect code from compiling.

2 Likes

I'm guessing you're trying to do something like this:

    for c in &mut iter {
        println!("{} ... {}", c, iter.as_str());
        i += 1;
        if i > 3 { break; }
    }

:arrow_right:


error[E0502]: cannot borrow `iter` as immutable because it is also borrowed as mutable
 --> src/main.rs:6:34
  |
5 |     for c in &mut iter {
  |              ---------
  |              |
  |              mutable borrow occurs here
  |              mutable borrow later used here
6 |         println!("{} ... {}", c, iter.as_str());
  |                                  ^^^^^^^^^^^^^ immutable borrow occurs here

(Documentation for the iterator I used in this example)

If so, you may be able to solve the borrow check error by using while let instead of for:

    while let Some(c) = iter.next() {
        println!("{} ... {}", c, iter.as_str());
        i += 1;
        if i > 3 { break; }
    }

Instead of consuming the iterator, or consuming a &mut to the iterator for the entirety of the loop, this loop borrows iter at the individual code points.


Incidentally, I think this is the third post about your iterator, but you have never posted your actual iterator code or something directly demonstrating your problem. It would be much easier to help if you did so.

6 Likes

I do appreciate quinedot's help. Every suggestion has been very helpful. My project has grown quite larger than what I initially intended, so I am only showing a snippet at a time; it is not because what I am doing is top secret.

Your suggested solution of

while let Some(char_val) = iterator.next() { ... }

sounds very good to me.

my source is now up