无法 hold/pass 将父级引用到组合对象
Unable to hold/pass reference of parent to the composition object
在 C++ 中,类似于 struct A
由 struct B
组成,B
的某些函数采用指向父对象 A
的指针。因此 A
的函数调用 B
的函数将简单地将 this
指针传递给它。我正在 Rust 中尝试这个,但未能让它工作 - 这就是我想要实现的目标:
struct A<Type: T> {
composition: Type,
value: usize,
}
impl<Type> A<Type> where Type: T {
fn new(obj: Type) -> A<Type> {
A {
composition: obj,
value: 999,
}
}
fn f(&mut self) {
println!("Value: {:?}", self.value);
}
fn call(&mut self) {
self.composition.f(&mut self);
}
}
trait T {
fn f(&mut self, &mut A<Self>);
}
struct B {
value: usize,
}
impl B {
fn new() -> B {
B { value: 0, }
}
}
impl T for B {
fn f(&mut self, parent: &mut A<B>) {
println!("B::f");
parent.f();
}
}
fn main() {
let objA = A::new(B::new());
// I want call sequence -> A::call() -> B::f() -> A::f()
objA.call();
}
请注意,我要求所有函数都具有可变性,尽管在上面的示例中,大多数函数参数中的 &mut self
似乎没有多大意义。它是如何在 Rust 中做到这一点的?
这行不通,因为您违反了可变别名要求 - 您试图同时可变地借用 A
及其子结构:
self.composition.f(self);
// roughtly equivalent to:
let c = &mut self.composition; // borrow substructure
c.f(self /* borrow self */);
(我删除了明确的 &mut self
,因为它不正确(因为它给了你 &mut &mut A<...>
,但它根本没有改变整个画面。)
这是 Rust 框架中的自然错误。假设 f
在此特定组合上的实现 X
重写了传递对象上的 composition
字段:
impl T for X {
fn f(&mut self, a: &mut A<X>) {
a.composition = create_x_somehow();
}
}
突然调用此方法的对象被销毁,self
失效!
自然地,编译器会阻止你这样做即使你知道你不修改composition
,因为这种知识不能静态编码(特别是考虑到这是一个特征方法,可以由任何有权访问你的特征的人来实现。
在这种情况下,您基本上有两种选择:
- 重新表述问题,使其不再需要这样的架构,或者
- 使用特殊的 language/library 构造来解决此类静态检查。
第二点是关于使用 Cell
/RefCell
之类的东西(它们是安全的,即不需要 unsafe
块,但它们可能会在运行时出现恐慌 - 可能这些可以在你的情况下工作)或者,如果没有其他帮助,请使用原始指针和 unsafe
代码。但是,坦率地说,第一种选择通常更好:如果您根据编译器强制执行的所有权语义和别名规则设计代码,几乎总是生成的架构质量会好得多。
在 C++ 中,类似于 struct A
由 struct B
组成,B
的某些函数采用指向父对象 A
的指针。因此 A
的函数调用 B
的函数将简单地将 this
指针传递给它。我正在 Rust 中尝试这个,但未能让它工作 - 这就是我想要实现的目标:
struct A<Type: T> {
composition: Type,
value: usize,
}
impl<Type> A<Type> where Type: T {
fn new(obj: Type) -> A<Type> {
A {
composition: obj,
value: 999,
}
}
fn f(&mut self) {
println!("Value: {:?}", self.value);
}
fn call(&mut self) {
self.composition.f(&mut self);
}
}
trait T {
fn f(&mut self, &mut A<Self>);
}
struct B {
value: usize,
}
impl B {
fn new() -> B {
B { value: 0, }
}
}
impl T for B {
fn f(&mut self, parent: &mut A<B>) {
println!("B::f");
parent.f();
}
}
fn main() {
let objA = A::new(B::new());
// I want call sequence -> A::call() -> B::f() -> A::f()
objA.call();
}
请注意,我要求所有函数都具有可变性,尽管在上面的示例中,大多数函数参数中的 &mut self
似乎没有多大意义。它是如何在 Rust 中做到这一点的?
这行不通,因为您违反了可变别名要求 - 您试图同时可变地借用 A
及其子结构:
self.composition.f(self);
// roughtly equivalent to:
let c = &mut self.composition; // borrow substructure
c.f(self /* borrow self */);
(我删除了明确的 &mut self
,因为它不正确(因为它给了你 &mut &mut A<...>
,但它根本没有改变整个画面。)
这是 Rust 框架中的自然错误。假设 f
在此特定组合上的实现 X
重写了传递对象上的 composition
字段:
impl T for X {
fn f(&mut self, a: &mut A<X>) {
a.composition = create_x_somehow();
}
}
突然调用此方法的对象被销毁,self
失效!
自然地,编译器会阻止你这样做即使你知道你不修改composition
,因为这种知识不能静态编码(特别是考虑到这是一个特征方法,可以由任何有权访问你的特征的人来实现。
在这种情况下,您基本上有两种选择:
- 重新表述问题,使其不再需要这样的架构,或者
- 使用特殊的 language/library 构造来解决此类静态检查。
第二点是关于使用 Cell
/RefCell
之类的东西(它们是安全的,即不需要 unsafe
块,但它们可能会在运行时出现恐慌 - 可能这些可以在你的情况下工作)或者,如果没有其他帮助,请使用原始指针和 unsafe
代码。但是,坦率地说,第一种选择通常更好:如果您根据编译器强制执行的所有权语义和别名规则设计代码,几乎总是生成的架构质量会好得多。