如何对值或其引用进行抽象化?(涉及IT技术)

4

我有一个特性,定义了一个可以保存值的对象接口。该特性有一种获取当前值的方法:

pub trait HasValue<T> {
    fn get_current_value(&self) -> &T;
}

这很好,但是我意识到,根据实际实现,有时候如果T被存储在一个字段中,返回一个引用会更方便;而有时候如果支持多线程共享(例如),那么返回T的克隆将更为方便。我正在努力想出如何在特质中表示这一点。我可以这样做:
pub enum BorrowedOrOwned<'a, T: 'a> {
    Borrowed(&'a T),
    Owned(T)
}

impl<'a, T: 'a> Deref for BorrowedOrOwned<'a, T> {
    type Target = T;

    fn deref(&self) -> &T {
        use self::BorrowedOrOwned::*;

        match self {
            &Borrowed(b) => b,
            &Owned(ref o) => o,
        }
    }
}

并将get_current_value()更改为返回BorrowedOrOwned<T>,但我不确定这是否符合惯用法。 BorrowedOrOwned<T>有点让我想起了Cow<T>,但由于Cow的目的是进行复制并写入,而我将放弃任何写入,因此似乎在语义上是错误的。

Cow<T>是抽象引用或拥有值的正确方式吗?除BorrowedOrOwned<T>之外还有更好的方法吗?


返回一个关联类型 B: Borrow<T> 是否可行? - Chris Emerson
@ChrisEmerson 我不确定这样做的权衡利弊是什么,所以我不确定。你能详细说明一下吗?这似乎是一个普遍有用的问题,所以我对所有答案都感兴趣,即使它们没有解决我的具体情况。 - Wesley Wiser
我认为实际上它并没有直接帮助;你最终仍然需要像“Cow”这样的东西。 - Chris Emerson
1个回答

9

我建议您使用Cow,因为您的BorrowedOrOwned除了没有更少的便利方法之外,与Cow没有区别。任何获得BorrowedOrOwned对象的人都可以匹配它并获取所拥有的值或可变引用。如果您想防止能够获取可变引用或对象本身的混淆,下面的解决方案也适用。

对于您的用例,我建议您仍然使用&T,因为没有理由使API更加复杂。如果用户想要一个usize,当Tusize时,他们可以简单地取消引用引用。

只有在您期望用户以拥有的方式实际处理对象时,才有意义拥有对象。即使如此,Cow的目的是抽象大型/重型对象,您通过所有权传递它,以便不需要任何人进行clone操作。您的用例相反,您希望通过所有权传递小对象,以防止用户需要复制小对象,而您正在复制它。


谢谢你的建议。在简化问题时,我错误地陈述了有时想返回&T,有时需要返回T的原因。如果实现将T存储在Arc<Refcell<T>>中(例如),那么就不可能简单地返回&T。必须克隆T,由于克隆体存在于函数的堆栈上,因此无法返回对其的引用。希望这有所帮助。PS. 在RBR与你交流很棒 :) - Wesley Wiser
1
啊!这样就更有意义了。那么我建议你的 BorrowedOrOwned 应该是 enum Borrow<'a, T> { Ref(&'a T), Arc(Arc<RefCell<T>>), Rc(Rc<RefCell<T>>) } 或者类似的东西。由于 RefCell 的存在,它变得复杂了,因为许多用例可能不想在其中使用 RefCell,但是如果不知道更多关于用例的信息,我猜这会解决问题。 - oli_obk
非常正确。我需要稍微尝试一下,但是我会将这个标记为答案,因为你已经回答了我的关于“Cow”的问题,这绝对指引了我正确的方向。谢谢。 - Wesley Wiser
1
术语“Cow”对于新手来说有点令人困惑,特别是当处理只读返回值时,您从未打算复制或写入。虽然我知道“Cow”,但我正在寻找类似于“MaybeOwned”的东西。但似乎“Cow”也用于此。 - ideasman42

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接