1

I have two simple structures like that:

struct Foo {
}

struct Bar<'a> {
    foo: &'a mut Foo;
}

impl Foo {
    pub fn new() -> Foo {
        Foo
    }
}

impl<'a> Bar<'a> {
    pub fn new(foo: &'a mut Foo) -> Bar<'a> {
        Bar {
            foo: foo,
        }
    }
}

So basically the structure Bar needs a reference to a Foo to work on. This is how I use it:

let mut foo = Foo::new();
let mut bar = Bar::new(&mut foo);

Now I want to create a "glue" structure to handle the creation of all these Foos and Bars. So I just want the structure Container to have 1 Foo, 1 Bar.

I tried doing something like:

impl Container {
    pub fn new() -> Container {
        Container {
          foo: Foo::new(),
          bar: Bar::new(&mut foo),
        }
    }
}

But it doesnt work, I cannot reference the field foo from the bar initializer. I basically want to mimic the following C++ code:

Container::Container() :
    foo(),
    bar(&foo)
{}

Any ideas on how to solve this? Of course using any kind of dynamic allocation/reference counting would be highly overkill here.

--- EDIT ---

To be clear, I want to create the Rust code equivalent to this C++ snippet:

struct Foo {
};

struct Bar {
    Bar(Foo& f) : foo(f) {}
    Foo& foo;
};

Foo f;
Bar b(f);

struct Container {
    Container() : f(), b(f) {}
    Foo f;
    Bar b;
};

--- EDIT ---

Here is what I ended up doing, using Rc<>.

use std::rc::Rc;

struct Foo {
  pub bar: Rc<Bar>,
}

impl Foo {
  pub fn new(bar: Rc<Bar>) -> Foo {
    Foo {
      bar: bar,
    }
  }
}

struct Bar {
  a: u8,
}

impl Bar {
  pub fn new() -> Bar {
    Bar {
      a: 42,
    }
  }
}

struct Container {
  pub bar: Rc<Bar>,
  pub foo: Foo,
}

impl Container {
  pub fn new() -> Container {
    let bar = Rc::new(Bar::new());
    Container {
      bar: bar.clone(),
      foo: Foo::new(bar.clone()),
    }
  }
}

fn main() {
  // Just checking that we get the same bar for both
  // inside Container and inside Foo
  let c = Container::new();
  println!("{:p}", &*c.bar);
  println!("{:p}", &*c.foo.bar);

  // So from what I understand, here bar is on the stack
  // then byte-copied to the heap where that Rc<> will point?
  let bar = Bar::new();
  println!("{:p}", &bar);
  let foo = Foo::new(Rc::new(bar));
  println!("{:p}", &*foo.bar);

  // Sad story is that using this Rc<> I now use the "cuteness"
  // and safety that I had with reference borrowing:
  // struct Foo<'a> {
  //   bar: &'a mut Bar,
  // }
  // impl<'a> Foo<'a> {
  //   pub fn new(bar: &'a Bar) -> Foo<'a> {
  //     Foo { bar: bar }
  //   }
  // }
  // let bar = Bar::new();
  // let foo = Foo::new(&bar);
}

But this is not really satisfying, I feel like I used a machine gun to kill a rabbit. Any insight greatly appreciated :(

NewbiZ
  • 2,395
  • 2
  • 26
  • 40
  • possible duplicate of [How can I provide a reference to a struct that is a sibling?](http://stackoverflow.com/questions/26349778/how-can-i-provide-a-reference-to-a-struct-that-is-a-sibling) – Shepmaster Jan 22 '15 at 15:09

2 Answers2

2

It is not possible to express this concept in safe code in Rust.

Chris Morgan
  • 86,207
  • 24
  • 208
  • 215
  • There has to be a "rusty" way to handle this case? Maybe I could use `Option`, and check in each function that there is something inside? – NewbiZ Jan 22 '15 at 15:30
  • I may misunderstand, but I think this *must* be unsafe because rust "moves" always involve memcpy's. So, if you have a function `fn takeContainer(c: Container) {...}`, the address of `c` in the body of this function is allowed be different than the address of `c` in the caller. This means that the address of `c.f` and `c.b` will also change. So the contents of `c.b` have to change when the value is moved. But that won't happen with a `memcpy`, so the reference would point to the wrong address. – Aidan Cully Jan 22 '15 at 16:33
1

Consider this simplified view of memory. I'm making up sizes and offsets to illustrate the problem, they bear no resemblance to reality.

We start by stack-allocating a brand-new Container with your desired structure at address 80.

80 Container
..   foo: Foo (size 8)
88   bar: Bar
       foo: &Foo (size 8, value 80)

Now we pass the structure by value into a method, or return it from where we created it, like a constructor would. Moving by value involves a bit-for-bit copy, so let's move it to address 40:

40 Container
..   foo: Foo (size 8)
48   bar: Bar
       foo: &Foo (size 8, value 80)

Uh oh! The inner foo now points to a piece of memory that isn't part of our struct anymore! That's the type of unsafe code that Rust tries to prevent.

Conceptually, there are some ways to prevent this. Rust could track that a pointer is really just some offset into itself and then rewrite the value every time the item is moved. I don't know, but that seems expensive. It might also need some special syntax to denote it.

A solution I would like to see would involve indirection and the heap. If we heap-allocated the top-level foo and if Rust tracked stack and heap lifetimes separately, then we could have something like:

== stack
80 Container
..   foo: Box<Foo> (size 8, value 10)
88   bar: Bar
       foo: &Foo (size 8, value 10)

== heap
10 Foo (size 8) 

Moving the structure would change the addresses, but the value would be safe-and-sound because Foo is off in the heap and not moving around.

I don't know that there are any plans to support this kind of solution, but I don't think there's been much clamor for it, either. Maybe it's not even technically feasible!

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • I get your point here, but I don't understand how this is related to the problem. Doesn't that just imply that `Container` is not implicitely byte-copyable, and thus should implement the `Copy` trait to handle this case? (and initialize the reference to point to the newly created other field) – NewbiZ Jan 23 '15 at 00:17
  • [`Copy`](http://doc.rust-lang.org/std/marker/trait.Copy.html) doesn't have any methods to implement, none of the marker traits do. `Copy` also specifically says: "Types that can be copied by simply copying bits". – Shepmaster Jan 23 '15 at 00:44
  • My bad, I misread the meaning of the `Copy` trait. I think your explanation is starting to find its way through my brain... thanks a lot. – NewbiZ Jan 23 '15 at 00:53
  • 1
    @AurélienVallée: it is less a matter of copy and more a matter of *move*. For simplicity reasons, all structures in Rust are byte-movable; this has huge advantages in terms of designing containers for example because 1/ it guarantees the operation is No Throw and 2/ it is easy to bulk-move (memcpy). To regain that, C++ containers have to have multiple execution paths depending on whether the move/copy is `noexcept` and whether a type is trivially movable/copyable; the alternatives are eliminated at compile-time thanks to monomorphisation, but that's a lot more complex. – Matthieu M. Jan 23 '15 at 07:55