我有两个简单的结构:
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,
}
}
}
所以基本上结构Bar
需要引用Foo
来处理。这就是我使用它的方式:
let mut foo = Foo::new();
let mut bar = Bar::new(&mut foo);
现在我想创建一个“粘合”结构来处理所有这些Foo
和Bar
的创建。所以我只希望结构Container
有1 Foo
,1 Bar
。
我尝试过这样的事情:
impl Container {
pub fn new() -> Container {
Container {
foo: Foo::new(),
bar: Bar::new(&mut foo),
}
}
}
但它不起作用,我无法从foo
初始值设定项中引用字段bar
。
我基本上想模仿以下C ++代码:
Container::Container() :
foo(),
bar(&foo)
{}
关于如何解决这个问题的任何想法?当然,使用任何类型的动态分配/引用计数都会非常过分。
---编辑---
要清楚,我想创建与此C ++代码段等效的Rust代码:
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;
};
---编辑---
以下是我最终使用的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);
}
但这并不令人满意,我觉得我用机枪杀了一只兔子。任何见解都非常感激:(
答案 0 :(得分:2)
无法在Rust中的安全代码中表达此概念。
答案 1 :(得分:1)
考虑这种简化的记忆观。我编写尺寸和偏移来说明问题,它们与现实没有任何相似之处。
我们首先通过堆栈分配一个全新的Container
,其中包含您在地址80处所需的结构。
80 Container
.. foo: Foo (size 8)
88 bar: Bar
foo: &Foo (size 8, value 80)
现在我们按值将结构传递给一个方法,或者从我们创建它的地方返回它,就像构造函数一样。按值移动涉及逐位复制,因此我们将其移至地址40:
40 Container
.. foo: Foo (size 8)
48 bar: Bar
foo: &Foo (size 8, value 80)
哦,哦!内部foo
现在指向一段记忆不再是我们结构的一部分!这是Rust试图阻止的不安全代码的类型。
从概念上讲,有一些方法可以防止这种情况发生。 Rust可以跟踪指针实际上只是某个偏移量,然后每次移动项目时重写该值。我不知道,但这看起来很贵。它可能还需要一些特殊的语法来表示它。
我希望看到的解决方案涉及间接和堆。如果我们堆分配顶级foo
并且Rust分别跟踪堆栈和堆生存期,那么我们可能会有类似的东西:
== stack
80 Container
.. foo: Box<Foo> (size 8, value 10)
88 bar: Bar
foo: &Foo (size 8, value 10)
== heap
10 Foo (size 8)
移动结构会改变地址,但是这个值会安然无恙,因为Foo
在堆中关闭而不会移动。
我不知道有任何支持这种解决方案的计划,但我也不认为它有很大的喧嚣。也许它甚至在技术上都不可行!