1

I want to write the equivalent of this C++ in Rust:

struct Bar { int x; };
struct Foo { Bar* bar; };

void main() {
    Bar bar { 42 };
    Foo foo { &bar };

    Bar bar2 { 50 };

    foo.bar = &bar2;
    printf("%d\n", foo.bar->x);
}

The C++ sets up a structure, then swaps out a Bar object with another Bar object. This is the code I've tried:

struct Bar(isize);
impl Bar {
    fn new(i: isize) -> Bar { Bar(i) }
}

struct Foo<'a> {
    bar: Option<&'a Bar>,
}
impl<'a> Foo<'a> {
    fn new(bar: &Bar) -> Foo {
        Foo { bar: Some(&bar) }
    }
}

fn main() {
    // Set up first state
    let bar = Bar::new(42);
    let mut foo = Foo::new(&bar);

    // Replace bar object
    let bar2 = Bar::new(50);
    foo.bar = Some(&bar2);

    if let Some(e) = foo.bar {
        println!("{}", e.0);
    }
}

This code complains:

error[E0597]: `bar2` does not live long enough
  --> src/main.rs:22:21
   |
22 |     foo.bar = Some(&bar2);
   |                     ^^^^ borrowed value does not live long enough
...
27 | }
   | - `bar2` dropped here while still borrowed
   |
   = note: values in a scope are dropped in the opposite order they are created

Is it possible in Rust to have a structure that has a reference to a sub-structure whose lifetime can be shorter (i.e. you can replace the sub-structure instance with another instance)?

I looked at some other answers, and tried to split up the structure:

struct Bar(isize);
impl Bar {
    fn new(i: isize) -> Bar {
        Bar(i)
    }
}

struct FooCore {
    a: isize,
}

struct Foo<'c, 'a> {
    core: &'c FooCore,
    bar: &'a Bar,
}
impl<'c, 'a> Foo<'c, 'a> {
    fn new(core: &'c FooCore, bar: &'a Bar) -> Foo<'c, 'a> {
        Foo {
            core: &core,
            bar: &bar,
        }
    }
}

fn main() {
    // Set up first state
    let core = FooCore { a: 10 };
    let bar = Bar::new(42);
    let _foo = Foo::new(&core, &bar);

    // Replace bar object
    let bar2 = Bar::new(50);
    let foo = Foo::new(&core, &bar2);

    println!("{} {}", foo.core.a, foo.bar.0);
}

This compiles and works, however, as soon as I make the core and bar fields mutable, it falls apart. Below is code that uses mut:

#![allow(unused_mut)]

struct Bar(isize);
impl Bar {
    fn new(i: isize) -> Bar {
        Bar(i)
    }
}

struct FooCore {
    a: isize,
}

struct Foo<'c, 'a> {
    core: &'c mut FooCore,
    bar: &'a mut Bar,
}
impl<'c, 'a> Foo<'c, 'a> {
    fn new(core: &'c mut FooCore, bar: &'a mut Bar) -> Foo<'c, 'a> {
        Foo {
            core: &mut core,
            bar: &mut bar,
        }
    }
}

fn main() {
    // Set up first state
    let mut core = FooCore { a: 10 };
    let mut bar = Bar::new(42);
    let mut _foo = Foo::new(&mut core, &mut bar);

    // Replace bar object
    let mut bar2 = Bar::new(50);
    let mut foo = Foo::new(&mut core, &mut bar2);

    println!("{} {}", foo.core.a, foo.bar.0);
}

This results in the errors:

error[E0597]: `core` does not live long enough
  --> src/main.rs:21:24
   |
21 |             core: &mut core,
   |                        ^^^^ borrowed value does not live long enough
...
24 |     }
   |     - borrowed value only lives until here
   |
note: borrowed value must be valid for the lifetime 'c as defined on the impl at 18:1...
  --> src/main.rs:18:1
   |
18 | impl<'c, 'a> Foo<'c, 'a> {
   | ^^^^^^^^^^^^^^^^^^^^^^^^

error[E0597]: `bar` does not live long enough
  --> src/main.rs:22:23
   |
22 |             bar: &mut bar,
   |                       ^^^ borrowed value does not live long enough
23 |         }
24 |     }
   |     - borrowed value only lives until here
   |
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 18:1...
  --> src/main.rs:18:1
   |
18 | impl<'c, 'a> Foo<'c, 'a> {
   | ^^^^^^^^^^^^^^^^^^^^^^^^

error[E0499]: cannot borrow `core` as mutable more than once at a time
  --> src/main.rs:35:33
   |
31 |     let mut _foo = Foo::new(&mut core, &mut bar);
   |                                  ---- first mutable borrow occurs here
...
35 |     let mut foo = Foo::new(&mut core, &mut bar2);
   |                                 ^^^^ second mutable borrow occurs here
...
38 | }
   | - first borrow ends here

How do I structure my data to achieve what I want?

Cthutu
  • 8,713
  • 7
  • 33
  • 49
  • 2
    the compiler help `note: values in a scope are dropped in the opposite order they are created` seems particularly valuable here. Sure you can "replace the sub-structure instance with another instance", but not with one that will be dropped while the 'parent' still references it. – turbulencetoo May 29 '18 at 20:19
  • 1
    @turbulencetoo Write it as an answer – Boiethios May 29 '18 at 20:45
  • 1
    Do the answers to https://stackoverflow.com/q/45713314/3650362 answer your question? (full disclosure: I wrote one) – trent May 30 '18 at 00:20
  • I agree with trentcl — I believe your question is answered by the answers of [Is it possible to have a struct which contains a reference to a value which has a shorter lifetime than the struct?](https://stackoverflow.com/q/45713314/155423). If you disagree, please [edit] your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster May 30 '18 at 01:56
  • 2
    Possible duplicate of [Is it possible to have a struct which contains a reference to a value which has a shorter lifetime than the struct?](https://stackoverflow.com/questions/45713314/is-it-possible-to-have-a-struct-which-contains-a-reference-to-a-value-which-has) – trent May 30 '18 at 02:43
  • None of the answers seem to answer my question. I tried breaking the structure into two parts, but it falls apart when you start making things mutable. – Cthutu May 30 '18 at 15:01
  • I edited the question. – Cthutu May 30 '18 at 15:11
  • *as soon as I make the `core` and `bar` fields mutable, it falls apart* — show how you've attempted this, and *include* the error message you get. "it falls apart" means nothing concrete to anyone but yourself. This is basic information that you **have** to share because we aren't mind readers. – Shepmaster May 30 '18 at 15:23
  • https://play.rust-lang.org/?gist=ba223661af5e7598c51895e73c5eba76&version=stable&mode=debug ? – Stargateur May 30 '18 at 15:43
  • https://play.rust-lang.org/?gist=7c5109baf8c2e81ecfdad407a37aabc5&version=stable&mode=debug – Cthutu May 30 '18 at 15:48
  • Your *new* problem is that `&mut core` is a mutable reference to a mutable reference -- the error message is unfortunately bad, but it's not the same problem you started with. If you [move `bar` and `core` instead of borrowing again](https://play.rust-lang.org/?gist=1d8a9f6684ffc33ed8a46488e106a7be&version=stable&mode=debug), you get yet another error message, still not related to the shortening of lifetimes, which [nevertheless goes away under `feature(nll)`](https://play.rust-lang.org/?gist=db8276cd83835ee42425aee7178122cd&version=nightly&mode=debug). I still believe this is a duplicate. – trent May 30 '18 at 16:27
  • This question is now specifically asking about mutable sub-structures. This is such a super-common handling of data. You want to replace a reference to an instance with another reference to an instance inside the structure, and the structure and instances must be mutable. I can't see how to do this common data manipulation easily in Rust. – Cthutu May 30 '18 at 17:12
  • The first error you're dealing with is a consequence of [Rust references not working like C++ ones](https://stackoverflow.com/questions/36335342/meaning-of-the-ampersand-and-star-symbols-in-rust). Once you fix that you get to [a problem that non-lexical lifetimes fixes](https://stackoverflow.com/q/45713314/155423). Neither of these is really "about mutable sub-structures" so I'm not sure how best to write an answer, and my inclination is to mark it a duplicate of the second one since that's a closer match to what you're actually trying to do. – trent May 30 '18 at 18:33

1 Answers1

2

The obvious problem is that you are taking a reference to a reference unnecesarily:

In this code, core and bar are already references, so no need to get its address:

impl<'c, 'a> Foo<'c, 'a> {
    fn new(core: &'c mut FooCore, bar: &'a mut Bar) -> Foo<'c, 'a> {
        Foo {
            core: &mut core,
            bar: &mut bar,
        }
    }
}

Write instead:

impl<'c, 'a> Foo<'c, 'a> {
    fn new(core: &'c mut FooCore, bar: &'a mut Bar) -> Foo<'c, 'a> {
        Foo {
            core: core,
            bar: bar,
        }
    }
}

About the subobject living longer than the main object, the general answer is "no, you can't", because the reference could be left dangling and Rust will not allow it. There are workarounds, as the linked answer in the comments above shows.

As an extra workaround to mimic your C code, you could write:

fn main() {
    // Set up first state
    let mut core = FooCore { a: 10 };
    let mut bar = Bar::new(42);
    let mut bar2; //lifetime of bar2 is greater than foo!
    let mut foo = Foo::new(&mut core, &mut bar);

    bar2 = Bar::new(50); //delayed initialization
    // Replace bar object
    foo.bar = &mut bar2;

    println!("{} {}", foo.core.a, foo.bar.0);
}
rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • Unfortunately delay initialisation won't work because the number of times the sub-object will be replaced is unbounded. – Cthutu May 31 '18 at 19:03
  • 1
    @Cthutu: Well, we'll need to see the C++ equivalent code with those unbounded replacements, to think of a possible workaround. – rodrigo May 31 '18 at 19:27
  • The actual C++ I am trying to recreate is large as I am trying to port an application. Its an emulator that has a structure called Nx, which has a reference to a Machine structure. In the emulator you can change to different variations of the machine, so what I want to do is destroy the old machine and recreate a new one with different parameters. This is a super common data idiom that Rust seems to struggle with. I understand the reasons why, but it seems that Rust isn't practical for my purposes. Shame, since I really wanted to get away from C/C++. – Cthutu May 31 '18 at 19:35
  • I did find this: https://rust-leipzig.github.io/architecture/2016/12/20/idiomatic-trees-in-rust/ – Cthutu May 31 '18 at 19:51
  • 1
    @Cthutu: Yes, I had a somewhat similar problem when porting code to Rust. I've found that for complex structures, a dynamic C++ pointer, `std::shared_ptr`, kind of maps to `Rc>`, or `Weak>` to break cycles`, or `Option>>` if nullable. – rodrigo May 31 '18 at 20:18