我想为
self
的某个领域设计一个具有可变和不可变吸气剂的特征。
但是,我希望 trait 的实现者只需要实现一个方法,通常是可变的 getter。
我找到了这个解决方案(playground):
pub trait MyTrait<T> {
fn inside_mut(&mut self) -> &mut T;
fn inside_ref(&self) -> &T {
let ptr = self as *const Self as *mut Self;
&* unsafe { &mut *ptr }.inside_mut()
}
}
pub struct Data(String);
impl MyTrait<String> for Data {
fn inside_mut(&mut self) -> &mut String { &mut self.0 }
}
fn main() {
let mut data = Data(format!("Foo"));
let x: &String = data.inside_ref();
let y: &String = data.inside_ref();
println!("x -> {x}");
println!("y -> {y}");
*data.inside_mut() = format!("Bar");
println!("data.0 -> {}", data.0);
}
结果:
Standard Error
Compiling playground v0.0.1 (/playground)
Finished release [optimized] target(s) in 0.93s
Running `target/release/playground`
Standard Output
x -> Foo
y -> Foo
data.0 -> Bar
我的问题:这种方法合理吗?
不,这不是声音。
用户可以在他的
self
实现中修改inside_mut
,这也会修改inside_ref
中的结构。
像这样:
pub trait MyTrait<T> {
fn inside_mut(&mut self) -> &mut T;
fn inside_ref(&self) -> &T {
let ptr = self as *const Self as *mut Self;
&*unsafe { (&mut *ptr).inside_mut() }
}
}
#[derive(Debug)]
struct MyStruct {
value: i32,
}
impl MyTrait<i32> for MyStruct {
fn inside_mut(&mut self) -> &mut i32 {
self.value += 1;
&mut self.value
}
}
fn main() {
// Important: this is not `mut`!!!
let x = MyStruct { value: 42 };
println!("{:?}", x);
x.inside_ref();
println!("{:?}", x);
}
MyStruct { value: 42 }
MyStruct { value: 43 }
这意味着这个实现是不健全的:
行为被认为是未定义的
- 改变不可变数据。
项目中的所有数据都是不可变的。此外,通过共享引用访问的所有数据或不可变绑定拥有的数据都是不可变的,除非该数据包含在const
.UnsafeCell<U>