我想在struct中添加一个字段,如下所示:
struct Foo<T> {
bar: Smart<T>
}
其中bar可以是Rc<T
或Weak<T>
,具体取决于Foo不同实例之间的“所有权关系”。除了创建自定义枚举之外,Rust中还有什么惯用的方法来做到这一点吗?
答案 0 :(得分:2)
除了创建自定义枚举以外,Rust中还有什么惯用的方法吗?
就像Rust中的其他大多数“此或该”选择一样,enum
是 惯用方式。
答案 1 :(得分:2)
彼得的答案表明,stdlib中没有这样的抽象。您可以定义一个小的枚举来处理这两种情况:
use std::rc::{Rc, Weak};
enum MaybeStrong<T> {
Strong(Rc<T>),
Weak(Weak<T>),
}
impl<T> MaybeStrong<T> {
fn get(&self) -> Option<Rc<T>> {
match self {
MaybeStrong::Strong(t) => Some(Rc::clone(t)),
MaybeStrong::Weak(w) => w.upgrade(),
}
}
}
struct Foo<T> {
bar: MaybeStrong<T>
}
impl<T> Foo<T> {
fn from_weak(inner: Weak<T>) -> Self {
Self { bar: MaybeStrong::Weak(inner) }
}
fn from_strong(inner: Rc<T>) -> Self {
Self { bar: MaybeStrong::Strong(inner) }
}
fn say(&self) where T: std::fmt::Debug {
println!("{:?}", self.bar.get())
}
}
fn main() {
let inner = Rc::new("foo!");
Foo::from_weak(Rc::downgrade(&inner)).say();
Foo::from_strong(inner).say();
}
self.bar()
将始终返回Some
(如果它是由强指针创建的),并且在None
悬空的情况下返回Weak
。请注意,由于get()
首先需要创建自己的Rc
,因此该方法无法返回&T
(包括Option<&T>
),因为该{{1} }可能会晃来晃去。这也意味着&T
的所有用户在处理过程中都会对内部值拥有一个很强的计数,从而在任何情况下都可以安全使用。
答案 2 :(得分:2)
这种构造通常被称为“两种”,并且有一个板条箱看起来可以解决一些常见的用例:https://docs.rs/either/1.5.3/either/
那你就可以写
struct Foo<T> {
bar: Either<Weak<T>, Rc<T>>
}
然后获取Option<Rc<T>>
的示例函数可能是:
impl <T> Foo<T> {
fn get_rc(self) -> Option<Rc<T>> {
self.bar
.map_left( |weak| weak.upgrade() )
.map_right( |v| Some(v) )
.into_inner()
}
}
然后可以像这样使用它:
fn main() {
let x = Rc::new(1);
let f_direct = Foo{ bar:Either::Right(x.clone()) };
println!("f_direct.get_rc() = {:?}", f_direct.get_rc());
let f_weak = Foo{ bar:Either::Left(Rc::downgrade(&x)) };
println!("f_weak.get_rc() = {:?}", f_weak.get_rc());
}
链接到操场上的完整示例: