我如何实现一个我不拥有的特性我没有的类型?

le_*_*_me 25 traits rust

我想实现这个Shl特性Vec,代码如下.这会使事情变得vec << 4可能,这对于它来说是个不错的选择vec.push(4).

use std::ops::Shl;

impl<T> Shl<T> for Vec<T> {
    type Output = Vec<T>;

    fn shl(&self, elem: &T) -> Vec<T> {
        self.push(*elem);
        *self
    }
}

fn main() {
    let v = vec![1, 2, 3];
    v << 4;
}
Run Code Online (Sandbox Code Playgroud)

编译失败,出现以下错误:

无法提供扩展实现,其中特征和类型都未在此包中定义[E0117]

要么

type参数T必须用作某些本地类型的类型参数(例如MyStruct<T>); 只有当前包中定义的特征才能为类型参数实现[E0210]

据我了解,我必须修补stdlib,更具体地来说是collections::vec板条箱.有没有其他方法可以更改此代码以成功编译?

Luq*_*man 35

虽然你不能完全这样做,但通常的解决方法是在你自己的类型中包装你想要的类型并在其上实现特征.

use somecrate::FooType;
use somecrate::BarTrait;

struct MyType(FooType);

impl BarTrait for MyType {
    fn bar(&self) {
        // use `self.0` here
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 附录:实现 Deref 特征以避免每次都输入 `&lt;MyType&gt;.0.foo` (6认同)
  • 2020 年这仍然是有效的方法吗? (3认同)
  • @max是的。这种“限制”是 Rust 如此优秀的语言的核心因素之一。所以这在 2020 年仍然有效,并且在 Rust 版本 1 中可能永远有效。 (3认同)
  • @ChristianBongiorno我想说元组结构更干净一些,并且更明显的是它打算成为一种新类型,而不是如果它有命名字段。 (3认同)

mal*_*rbo 12

这会使事情变得vec << 4可能,这对于它来说是个不错的选择vec.push(4).

尽管可以这样做,但实现具有意外语义的运算符通常是一个坏主意.

以下是如何完成此操作的示例:

use std::ops::Shl;

struct BadVec<T>(Vec<T>);

impl<T> Shl<T> for BadVec<T> {
    type Output = BadVec<T>;

    fn shl(mut self, elem: T) -> Self::Output {
        self.0.push(elem);
        self
    }
}

fn main() {
    let mut v = BadVec(vec![1, 2, 3]);
    v = v << 4;
    assert_eq!(vec![1, 2, 3, 4], v.0)
}
Run Code Online (Sandbox Code Playgroud)

如果你实现Deref(DerefMut):

use std::ops::{Deref, DerefMut};

impl<T> Deref for BadVec<T> {
    type Target = Vec<T>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<T> DerefMut for BadVec<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}
Run Code Online (Sandbox Code Playgroud)

你可以调用Vec方法:

fn main() {
    let mut v = BadVec(vec![1, 2, 3]);
    v = v << 4;
    v.truncate(2);
    assert_eq!(2, v.len());
}
Run Code Online (Sandbox Code Playgroud)

看看newtype_derive箱子,它可以为你生成一些样板代码.

  • 官方文档中有一条信息丰富的注释:“为智能指针实现 `Deref` 可以方便地访问它们背后的数据,这就是他们实现 `Deref` 的原因。另一方面,关于 `Deref` 和 `DerefMut` 的规则是专门为适应智能指针而设计。因此,“Deref”应该仅针对智能指针实现,以避免混淆。” https://doc.rust-lang.org/stable/std/ops/trait.Deref.html (2认同)

snn*_*snn 11

这是有意禁止的,被称为“孤儿规则”。这是为了保证特质的一致性。根据孤儿规则,除非您是定义该结构的板条箱,或者是定义该特征的板条箱,否则您不能为结构提供特征的实现。如果你能实现你所要求的,我们就会有相互冲突的实现。

不过有一些解决方法,最常见的一种是新类型模式。在 newtype 模式中,我们将外部类型包装在本地结构中,并通过这个包装器实现所需的方法。

// External struct
use foo::Foo;

// Create a new type.
pub struct Bar(Foo);

// Provide your own implementations
impl Bar {
    pub fn new() -> Self {
        //..
    }
}
Run Code Online (Sandbox Code Playgroud)

欲了解更多信息,请查看这些参考资料: