Bizarre read-only facade

For background, I had a situation where because I want to support iterators, that one of my error tracking data structure becomes mutably borrowed during a "for loop" that it is basically not available until the end of the loop.

I just thought one potential workaround that even sounded strange to me. The idea is to hide all mutable states one level below my error tracking data structure, to make sure there are no aliases to my error states. Then I can vend out my read-only facade to the iterator as a shared borrow, even though the API will have obvious mutable sounding methods like reset_error_indication(). Will this resolve my mutable borrow issue?

It's hard to understand exactly what design you're proposing. Can you write a small code sample that defines the types and mutability you're thinking of, concretely in Rust rather than in informal words?

4 Likes

// read-only struct, lots of non-mutable references could points here
pub struct ByteConverter<'p> {
    my_sto: &'p mut ConverterState,
}

// 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] + 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
    }
}

impl<'f> ByteConverter<'f> {

    pub fn new(sto: &'f mut ConverterState) -> ByteConverter {
        ByteConverter {
            my_sto: sto,
        }
    }

    pub fn sum(& self) -> u32 {
        self.my_sto.sum()
    }

    pub fn signal_invalid_sequence(& self) {
        self.my_sto.signal_invalid_sequence();
    }

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

pub fn main() {
    let cs = ConverterState::new();
    let bc = ByteConverter::new(& mut cs);
    println!("{}", bc.sum());
}

This does not compile though.

A &'x &'y mut T is effectively a &'x &'x T. You can't call functions/methods that require a &mut T if all you have is a & &mut T. The &mut T itself is also unusable until all the & &mut T that reference it have expired; alternatively phrased, using the &mut T kills all outstanding borrows (& &mut T) of it.

Your example doesn't show the iteration case; if you follow the compiler advice, you'll eventually get a playground that compiles (but presumably doesn't meet your needs in some other way).

Incidentally you also have a logic error here:

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

You add together the two u8 before casting to a u32, so the addition may overflow and the result will never be greater than 255. Instead you should:

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

Ok. I agree this idea will not work. Rustc compiler said

'self' is a '&' reference, so the data it refers to cannot be borrowed as mutable.