可以在外国类型上实现特征吗?
Is possible to implement traits on foreign types?
我想实现向量与标量的频繁数学乘法:k * v = (k * v0, k * v1, k* v2..)
。代码如下:
#[derive(Debug)]
struct V(Vec<i32>);
impl std::ops::Mul<V> for i32 {
type Output = V;
fn mul(self, v: V) -> V {
V(v.0.iter().map(|v| v * self).collect())
}
}
fn main() {
let v = V(vec![1,2]);
println!("{:?}", 3 * v);
}
这个解决方案有效,但是我想避免定义结构 V,而是编写类似这样的代码:
impl std::ops::Mul<Vec<i32>> for i32 {
type Output = Vec<i32>;
fn mul(self, v: Vec<i32>) -> Vec<i32> {
v.iter().map(|v| v * self).collect()
}
}
抛出以下错误:
only traits defined in the current crate can be implemented for arbitrary types
--> src/main.rs:45:1
|
45 | impl std::ops::Mul<Vec<i32>> for i32 {
| ^^^^^-----------------------^^^^^---
| | | |
| | | `i32` is not defined in the current crate
| | `Vec` is not defined in the current crate
| impl doesn't use only types from inside the current crate
|
= note: define and implement a trait or new type instead
是否可以对外部类型使用乘法特征?
简而言之,没有。
您 运行 进入了“孤儿规则”。基本思想是,如果你想在 struct/enum/union Y
、 上实现一个特征 X
,至少其中一个 必须定义在当前箱子。
这有时有点限制,但它是 Rust“一致性规则”的产物。通常,特征和类型的每个组合最多只能有 1 个实现。所以下面的代码是无效的:
struct Wrapper<T>(T);
trait Print {
fn print(&self);
}
impl Print for Wrapper<i32> {
fn print(&self) {
println!("a very special int: {}", self);
}
}
impl<T: Display> Print for Wrapper<T> {
fn print(&self) {
print!("a normal type: {}", self);
}
}
假设您在代码中编写 0i32.print()
。 rustc 选择哪种实现?答案不明确(在 pre-specialization 世界中),因此鉴于 Rust 的显式原则,代码被彻底拒绝。
孤立规则是一种通常使生态系统更有用的机制。打破孤儿规则(如果可能的话)本身并没有什么不妥,但是,它会导致一个不幸的场景,你可能会使用 2 个 crate,它们为某些外部类型和特征定义了自己的实现。
想象一下,例如,一个板条箱决定添加这个(看起来相当合理的)实现:
impl<T> Display for Vec<T> where T: Display {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
todo!()
}
}
这很好,直到您要导入另一个 crate 也定义了类似的 impl。即使代码相同,它们也将是不同的实现,因此会不连贯并且无法编译。
孤立规则保护生态系统免受此类问题的影响。
包装器在 Rust 中是惯用的,不会强加运行时成本。如果你发现自己写了很多东西,我已经写了一个库来自动化很多样板文件,叫做 microtype
这可能很有用。
我想实现向量与标量的频繁数学乘法:k * v = (k * v0, k * v1, k* v2..)
。代码如下:
#[derive(Debug)]
struct V(Vec<i32>);
impl std::ops::Mul<V> for i32 {
type Output = V;
fn mul(self, v: V) -> V {
V(v.0.iter().map(|v| v * self).collect())
}
}
fn main() {
let v = V(vec![1,2]);
println!("{:?}", 3 * v);
}
这个解决方案有效,但是我想避免定义结构 V,而是编写类似这样的代码:
impl std::ops::Mul<Vec<i32>> for i32 {
type Output = Vec<i32>;
fn mul(self, v: Vec<i32>) -> Vec<i32> {
v.iter().map(|v| v * self).collect()
}
}
抛出以下错误:
only traits defined in the current crate can be implemented for arbitrary types
--> src/main.rs:45:1
|
45 | impl std::ops::Mul<Vec<i32>> for i32 {
| ^^^^^-----------------------^^^^^---
| | | |
| | | `i32` is not defined in the current crate
| | `Vec` is not defined in the current crate
| impl doesn't use only types from inside the current crate
|
= note: define and implement a trait or new type instead
是否可以对外部类型使用乘法特征?
简而言之,没有。
您 运行 进入了“孤儿规则”。基本思想是,如果你想在 struct/enum/union Y
、 上实现一个特征 X
,至少其中一个 必须定义在当前箱子。
这有时有点限制,但它是 Rust“一致性规则”的产物。通常,特征和类型的每个组合最多只能有 1 个实现。所以下面的代码是无效的:
struct Wrapper<T>(T);
trait Print {
fn print(&self);
}
impl Print for Wrapper<i32> {
fn print(&self) {
println!("a very special int: {}", self);
}
}
impl<T: Display> Print for Wrapper<T> {
fn print(&self) {
print!("a normal type: {}", self);
}
}
假设您在代码中编写 0i32.print()
。 rustc 选择哪种实现?答案不明确(在 pre-specialization 世界中),因此鉴于 Rust 的显式原则,代码被彻底拒绝。
孤立规则是一种通常使生态系统更有用的机制。打破孤儿规则(如果可能的话)本身并没有什么不妥,但是,它会导致一个不幸的场景,你可能会使用 2 个 crate,它们为某些外部类型和特征定义了自己的实现。
想象一下,例如,一个板条箱决定添加这个(看起来相当合理的)实现:
impl<T> Display for Vec<T> where T: Display {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
todo!()
}
}
这很好,直到您要导入另一个 crate 也定义了类似的 impl。即使代码相同,它们也将是不同的实现,因此会不连贯并且无法编译。
孤立规则保护生态系统免受此类问题的影响。
包装器在 Rust 中是惯用的,不会强加运行时成本。如果你发现自己写了很多东西,我已经写了一个库来自动化很多样板文件,叫做 microtype
这可能很有用。