为新类型实施 Deref 是否被认为是一种不好的做法?
Is it considered a bad practice to implement Deref for newtypes?
我经常用newtype模式,但是写腻了my_type.0.call_to_whatever(...)
。我很想实现 Deref
特性,因为它允许编写更简单的代码,因为在某些情况下我可以使用我的新类型,就好像它是基础类型一样,例如:
use std::ops::Deref;
type Underlying = [i32; 256];
struct MyArray(Underlying);
impl Deref for MyArray {
type Target = Underlying;
fn deref(&self) -> &Self::Target {
&self.0
}
}
fn main() {
let my_array = MyArray([0; 256]);
println!("{}", my_array[0]); // I can use my_array just like a regular array
}
这是好事还是坏事?为什么?缺点是什么?
the rules regarding Deref
and DerefMut
were designed specifically to accommodate smart pointers. Because of this, Deref
should only be implemented for smart pointers to avoid confusion.
我认为这是一个不好的做法。
since I can use my newtype as if it were the underlying type in some situations
这就是问题所在 — 它可以隐式用作基础类型 只要 引用是。如果您实现 DerefMut
,那么它也适用于需要可变引用的情况。
您无法控制基础类型中可用的内容和不可用的内容;一切都是。在你的例子中,你想让人们打电话给 as_ptr
? What about sort
吗?我当然希望你这样做,因为他们可以!
您所能做的就是尝试覆盖方法,但它们仍然必须存在:
impl MyArray {
fn as_ptr(&self) -> *const i32 {
panic!("No, you don't!")
}
}
即便如此,它们仍然可以被显式调用 (<[i32]>::as_ptr(&*my_array);
)。
出于同样的原因,我认为这是不好的做法,我认为使用继承来重用代码是不好的做法。在您的示例中,您实际上是从数组继承的。我永远不会写像下面这样的东西 Ruby:
class MyArray < Array
# ...
end
这又回到了面向对象建模中的 is-a 和 has-a 概念。 MyArray
是数组吗?它是否可以 数组可以 的任何地方使用?它是否具有对象应支持消费者不应该破坏的先决条件?
but I am tired of writing my_type.0.call_to_whatever(...)
与其他语言一样,我认为正确的解决方案是组合而不是继承。如果您需要转接电话,请在新类型上创建一个方法:
impl MyArray {
fn call_to_whatever(&self) { self.0.call_to_whatever() }
}
在 Rust 中造成这种痛苦的主要原因是缺少 委托。 假设的 委托语法可能类似于
impl MyArray {
delegate call_to_whatever -> self.0;
}
在等待第一个 class 委托时,我们可以使用像 delegate or ambassador 这样的箱子来帮助填补一些空白。
所以什么时候应该使用Deref
/DerefMut
?我提倡只有在您实施 智能指针 .
时才有意义
实际上,我确实使用Deref
/DerefMut
用于未公开暴露的新类型我是唯一或主要贡献者的项目。这是因为我相信自己并且非常了解我的意思。如果存在委托语法,我不会。
与接受的答案相反,我发现一些流行的 crates 为新类型而不是智能指针的类型实现 Deref
:
actix_web::web::Json<T>
是 (T,)
的元组结构,它 implements Deref<Target=T>
.
bstr::BString
有一个字段键入 Vec<u8>
并且 implements Deref<Target=Vec<u8>>
.
所以,只要不被滥用,也许就可以了,例如模拟多级继承层次结构。我还注意到上面的两个示例要么有零个 public 方法,要么只有一个 into_inner
方法,其中 returns 是内部值。保持包装器类型的方法数量最少似乎是个好主意。
我经常用newtype模式,但是写腻了my_type.0.call_to_whatever(...)
。我很想实现 Deref
特性,因为它允许编写更简单的代码,因为在某些情况下我可以使用我的新类型,就好像它是基础类型一样,例如:
use std::ops::Deref;
type Underlying = [i32; 256];
struct MyArray(Underlying);
impl Deref for MyArray {
type Target = Underlying;
fn deref(&self) -> &Self::Target {
&self.0
}
}
fn main() {
let my_array = MyArray([0; 256]);
println!("{}", my_array[0]); // I can use my_array just like a regular array
}
这是好事还是坏事?为什么?缺点是什么?
the rules regarding
Deref
andDerefMut
were designed specifically to accommodate smart pointers. Because of this,Deref
should only be implemented for smart pointers to avoid confusion.
我认为这是一个不好的做法。
since I can use my newtype as if it were the underlying type in some situations
这就是问题所在 — 它可以隐式用作基础类型 只要 引用是。如果您实现 DerefMut
,那么它也适用于需要可变引用的情况。
您无法控制基础类型中可用的内容和不可用的内容;一切都是。在你的例子中,你想让人们打电话给 as_ptr
? What about sort
吗?我当然希望你这样做,因为他们可以!
您所能做的就是尝试覆盖方法,但它们仍然必须存在:
impl MyArray {
fn as_ptr(&self) -> *const i32 {
panic!("No, you don't!")
}
}
即便如此,它们仍然可以被显式调用 (<[i32]>::as_ptr(&*my_array);
)。
出于同样的原因,我认为这是不好的做法,我认为使用继承来重用代码是不好的做法。在您的示例中,您实际上是从数组继承的。我永远不会写像下面这样的东西 Ruby:
class MyArray < Array
# ...
end
这又回到了面向对象建模中的 is-a 和 has-a 概念。 MyArray
是数组吗?它是否可以 数组可以 的任何地方使用?它是否具有对象应支持消费者不应该破坏的先决条件?
but I am tired of writing
my_type.0.call_to_whatever(...)
与其他语言一样,我认为正确的解决方案是组合而不是继承。如果您需要转接电话,请在新类型上创建一个方法:
impl MyArray {
fn call_to_whatever(&self) { self.0.call_to_whatever() }
}
在 Rust 中造成这种痛苦的主要原因是缺少 委托。 假设的 委托语法可能类似于
impl MyArray {
delegate call_to_whatever -> self.0;
}
在等待第一个 class 委托时,我们可以使用像 delegate or ambassador 这样的箱子来帮助填补一些空白。
所以什么时候应该使用Deref
/DerefMut
?我提倡只有在您实施 智能指针 .
实际上,我确实使用Deref
/DerefMut
用于未公开暴露的新类型我是唯一或主要贡献者的项目。这是因为我相信自己并且非常了解我的意思。如果存在委托语法,我不会。
与接受的答案相反,我发现一些流行的 crates 为新类型而不是智能指针的类型实现 Deref
:
actix_web::web::Json<T>
是(T,)
的元组结构,它 implementsDeref<Target=T>
.bstr::BString
有一个字段键入Vec<u8>
并且 implementsDeref<Target=Vec<u8>>
.
所以,只要不被滥用,也许就可以了,例如模拟多级继承层次结构。我还注意到上面的两个示例要么有零个 public 方法,要么只有一个 into_inner
方法,其中 returns 是内部值。保持包装器类型的方法数量最少似乎是个好主意。