Iterating over struct array and mutating values

To continue from the answers in the following post I have got stuck again. When trying to get the test below to work im getting the error:

cannot assign to data in a `&` reference

I do understand why I get this error but how to work my way out of it into a workable solution gets me confused.

The reason I get confused is that its not clear what needs to be mutable and when it has to be declare that is should be mutable.
For example in the Container struct I would like to do something like:
g: Box<[Option<mut Container>]>, but that is not even valid rust syntax.

mod novice {
    // Note: no Copy because `Box` doesn't implement `Copy`
    #[derive(Debug, Clone)]
    pub struct Container {
        // note that because Java has nullable pointers, it is necessary
        // to use `Option` to represent `null`
        // also, this isn't Box<[Option<Container>; 1000]> because
        // arrays past with a length greater than 32 don't implement any traits
        // from the standard library.
        g: Box<[Option<Container>]>,
        n: isize,
        x: f64,
    }

    impl Container {
        pub fn new() -> Container {
            Container {
                // changed to get rid of unconditional recursion and
                // better represent what Java is doing, also because `Container`
                // is not `Copy`, we need construct the boxed slice in other ways
                g: vec![None; 1000].into(),
                n: 1,
                x: 0.0,
            }
        }

        fn get(&self, index: usize) -> Option<&Container> {
            if index == 0 {
                Some(self)
            } else {
                self.g[index].as_ref()
            }
        }

        pub fn get_amount(&self) -> f64 {
            self.x
        }

        pub fn add_water(&mut self, x: f64) {
            let y = x / self.n as f64;
            for i in 0..self.g.len() {
                self.get(i).unwrap().x = self.get(i).unwrap().x + y;
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn get_amount() {
        let sut = novice::Container::new();
        assert_eq!(0.0, sut.get_amount());
    }
}

In rust you don't specify mutability when you declare the type but when you make a value of that type (*), isn't it the same in java?
That's why you can't do g: Box<[Option<mut Container>]>, if your variable is mutable, everything inside it will be mutable too.

I made a playground with all the mut added. Also if you haven't, you should consider reading the book.

(*) It's a simplified view, they are ways to make immutable fields and interior mutability let you mutate stuff declared immutable,...

1 Like

Mutability in Rust comes in two shapes:

  • either the binding (i.e., the variable holding the value) is declared mutable with:

    let mut x: _ = ...; or fn foo(mut x: _, ..) -> _.

  • or you use a unique / non-aliased reference / pointer to the value, noted &mut _:

    let at_x: &mut _ = ...; or fn(at_x: &mut _, ..) -> _

You can find more information in the Rust Book, or in my (WiP) blog post about it

So in your example the problem comes from the get() method taking and returning shared references &_ instead of unique references &mut _

2 Likes

As far as I know, you can't differentiate from mutable/immutable in java, unless you're using strings, but that's my C# talking, which is pretty close to java so I'm not too sure.

So im trying to implement the next function and things are starting to get very nasty. Im sure there are smarter ways but I want this novice impl to be very naive. So here we go.

        pub fn connect_to(&mut self, c: &mut Container) {
            let z = (self.x * self.n as f64 + c.x * c.n as f64) / (self.n + c.n) as f64;
            for i in 0..self.n as usize {
                for j in 0..c.n as usize {
                    if let Some(container) = self.get(i) {
                        if let Some(inner_container) = container.get(self.n + j) {
                            if let Some(connected_container) = c.get(j) {
                                inner_container = connected_container;
                            }
                        }
                    }
                    }
                }
        }

Its not the full connect_to function but its enogh for me to get stuck with the following errors:

error[E0503]: cannot use `self.n` because it was mutably borrowed
  --> src/lib.rs:55:70
   |
54 |                     if let Some(container) = self.get(i) {
   |                                              ---- borrow of `*self` occurs here
55 |                         if let Some(inner_container) = container.get(self.n + j) {
   |                                                                  --- ^^^^^^ use of borrowed `*self`
   |                                                                  |
   |                                                                  borrow used by call, in later iteration of loop

error[E0384]: cannot assign twice to immutable variable `inner_container`
  --> src/lib.rs:57:33
   |
55 |                         if let Some(inner_container) = container.get(self.n + j) {
   |                                     ---------------
   |                                     |
   |                                     first assignment to `inner_container`
   |                                     help: make this binding mutable: `mut inner_container`
56 |                             if let Some(connected_container) = c.get(j) {
57 |                                 inner_container = connected_container;
   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot assign twice to immutable variable

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.