2 个版本

0.1.1 2023年2月22日
0.1.0 2023年2月12日

#1649Rust 模式

Download history 11/week @ 2024-03-27 23/week @ 2024-04-03 88/week @ 2024-04-10 5/week @ 2024-05-22 16/week @ 2024-05-29 67/week @ 2024-06-05 44/week @ 2024-06-12 29/week @ 2024-06-19 29/week @ 2024-06-26

每月 172 次下载

MIT 许可证

9KB

Newtype 特质

Build Status Latest Version Rust Documentation GitHub license

问题

有时你想要将类型包装在新类型中,但又希望新类型实现与包装类型相同的特质。新类型特质可以帮助你自动为新类型实现包装类型的特质。

解决方案

当你定义一个特质时,你可以支持新类型特质,并且任何实现了新类型特质的数据类型都将自动实现你的特质。

use the_newtype::Newtype;
use derive_more::AsRef;

pub trait MyTrait {
    fn my_method(&self) -> String;
}

impl<T> MyTrait for T
where
    T: Newtype + AsRef<T::Inner>,
    T::Inner: MyTrait,
{
    fn my_method(&self) -> String {
        self.as_ref().my_method()
    }
}

// Now we can use the `MyTrait` trait for the newtype.

struct Foo;

impl MyTrait for Foo {
    fn my_method(&self) -> String {
        "foo".to_string()
    }
}

#[derive(AsRef)]
struct Bar(Foo);

impl Newtype for Bar {
    type Inner = Foo;
}

fn main() {
    let bar = Bar(Foo);
    assert_eq!(bar.my_method(), "foo");
}

何时使用

当你想要将类型包装在新类型中并且希望新类型实现所有新类型支持的包装类型特质时,可以使用新类型特质。如果你需要某些特质,你应该手动实现它们,并避免使用新类型特质。

缺点

新类型特质不适合以下情况:

  • 如果你想为实现了其他特质的所有类型实现一个特质(例如,每个实现了 Fancy 特质的类型都将实现 Awesome 特质),则每个特质只能有一个通用实现。在这种情况下,不能使用新类型特质。
use the_newtype::Newtype;

trait Fancy {
    fn fancy_method(&self) -> String;
}

// it's ok to implement the `Fancy` trait for the `Newtype` trait

impl<T> Fancy for T
where
    T: Newtype + AsRef<T::Inner>,
    T::Inner: Fancy,
{
    fn fancy_method(&self) -> String {
        self.as_ref().fancy_method()
    }
}

trait Awesome {
    fn awesome_method(&self) -> String;
}

// every type that implements the `Fancy` trait will implement the `Awesome` trait
// it's not possible to implement the `Awesome` trait for the `Newtype` trait

impl<T> Awesome for T
where
    T: Fancy,
{
    fn awesome_method(&self) -> String {
        let fancy = self.fancy_method();
        format!("{} is awesome!", fancy)
    }
}

技巧

使用 derive_moreNewtype

你可以使用 derive_more 包来为新类型实现 AsRefAsMutInto 特质。并且你可以使用 Newtype 宏来实现新类型的新类型特质。

use the_newtype::Newtype;
use derive_more::AsRef;

#[derive(AsRef, Newtype)]
struct Bar(String);

如何为 &self 实现特质

如果你想为 &self 实现一个特质,你可以使用 AsRef 特质。

use the_newtype::Newtype;

trait MyTrait {
    fn my_method(&self) -> String;
}

impl<T> MyTrait for T
where
    T: Newtype + AsRef<T::Inner>,
    T::Inner: MyTrait,
{
    fn my_method(&self) -> String {
        self.as_ref().my_method()
    }
}

如何为 &mut self 实现特质

如果你想为 &mut self 实现一个特质,你可以使用 AsMut 特质。

use the_newtype::Newtype;

trait MyTrait {
    fn my_method(&mut self) -> String;
}

impl<T> MyTrait for T
where
    T: Newtype + AsMut<T::Inner>,
    T::Inner: MyTrait,
{
    fn my_method(&mut self) -> String {
        self.as_mut().my_method()
    }
}

如何为 self 实现特质

如果您想为 self 实现一个特质,可以使用 Into 特质。

use the_newtype::Newtype;

trait MyTrait {
    fn my_method(self) -> String;
}

impl<T> MyTrait for T
where
    T: Newtype + Into<T::Inner>,
    T::Inner: MyTrait,
{
    fn my_method(self) -> String {
        self.into().my_method()
    }
}

如何不使用 self 实现特质

如果您想不使用 self 实现特质,不需要额外的特质。

use the_newtype::Newtype;


trait MyTrait {
    fn my_method() -> String;
}

impl<T> MyTrait for T
where
    T: Newtype,
    T::Inner: MyTrait,
{
    fn my_method() -> String {
        T::Inner::my_method()
    }
}

如何结合 self&self&mut self

如果您想为 self&self&mut self 实现特质,可以使用 IntoAsRefAsMut 特质共同使用。

use the_newtype::Newtype;

trait MyTrait {
    fn my_method(&self) -> String;
    fn my_method_mut(&mut self) -> String;
}

impl<T> MyTrait for T
where
    T: Newtype + AsRef<T::Inner> + AsMut<T::Inner>,
    T::Inner: MyTrait,
{
    fn my_method(&self) -> String {
        self.as_ref().my_method()
    }

    fn my_method_mut(&mut self) -> String {
        self.as_mut().my_method_mut()
    }
}

安装

[dependencies]
the-newtype = "0.1"

依赖

~3.5MB
~75K SLoC