【问题标题】:How can I denote a field that can be either Rc<T> or Weak<T>如何表示可以是 Rc<T> 或 Weak<T> 的字段
【发布时间】:2019-12-02 16:54:02
【问题描述】:

我想在结构中有一个字段,如下所示:

struct Foo<T> {
    bar: Smart<T>
}

其中 bar 可以是 Rc&lt;TWeak&lt;T&gt;,具体取决于 Foo 的不同实例之间的“所有权关系”。除了创建自定义枚举之外,Rust 中是否有任何惯用的方法来做到这一点?

【问题讨论】:

  • 我不认为存在这样的类型,但我不确定这是一个好主意:weak vs rc 允许对所有权关系进行建模。例如,父级拥有一个子级 (Rc&lt;Child&gt;),而子级指向其所有者:Weak&lt;Parent&gt;。无法区分这种根本差异可能会令人困惑。

标签: rust


【解决方案1】:

除了创建自定义枚举之外,Rust 中是否有任何惯用的方法来做到这一点?

就像 Rust 中的大多数其他“这个或那个”选择一样,enum 惯用的方式。

【讨论】:

  • OP 的问题 (IIUC) 是:std.手动编写这样的枚举可能需要大量样板文件。
  • @FrenchBoiethios std 没有提供类似的东西。
【解决方案2】:

彼得的回答建议,标准库中没有这样的抽象。您可以定义一个小枚举来处理这两种情况:

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,因此该方法无法返回&amp;T(包括Option&lt;&amp;T&gt;),因为&amp;T 可能是悬空的。这也意味着bar()的所有用户在处理时都将拥有一个强大的内在价值,使其在任何情况下都可以安全使用。

【讨论】:

  • 由于get() 总是暗示内存写入(递增/递减计数器),提供一个不需要在@ 中执行这些写入的就地apply(f: FnOnce(Option&lt;&amp;T&gt;) -&gt; R) -&gt; R 方法可能很有价值987654334@案例。特征绑定需要for&lt;'a&gt;
【解决方案3】:

这种构造通常被称为“Either”,并且有一个看起来可以解决一些常见用例的 crate:https://docs.rs/either/1.5.3/either/

那么你可以写

struct Foo<T> {
    bar: Either<Weak<T>, Rc<T>>
}

那么获取Option&lt;Rc&lt;T&gt;&gt; 的示例函数可能是:

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());
}

在操场上完成示例的链接:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c20faaa46277550e16a3d3b24f3d1750

【讨论】:

  • @MatthieuM。我不认为其中任何一个都会出现在游乐场的板条箱列表中 - 但事实证明它是。所以我已经修复了代码(是一个小错误)并链接到一个完整的示例。谢谢。
猜你喜欢
  • 2015-10-19
  • 1970-01-01
  • 2023-01-20
  • 2022-08-16
  • 2022-08-15
  • 2019-12-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多