2 个版本
0.1.1 | 2023年2月22日 |
---|---|
0.1.0 | 2023年2月12日 |
#1649 在 Rust 模式
每月 172 次下载
9KB
Newtype
特质
问题
有时你想要将类型包装在新类型中,但又希望新类型实现与包装类型相同的特质。新类型特质可以帮助你自动为新类型实现包装类型的特质。
解决方案
当你定义一个特质时,你可以支持新类型特质,并且任何实现了新类型特质的数据类型都将自动实现你的特质。
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_more
和 Newtype
宏
你可以使用 derive_more
包来为新类型实现 AsRef
、AsMut
和 Into
特质。并且你可以使用 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
实现特质,可以使用 Into
、AsRef
或 AsMut
特质共同使用。
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