如何为自我的不同可变性实现特征
How to implement a trait for different mutabilities of self
给出的是一个带有 u32
字段的结构。它只有一个重载方法,可以同时用作字段的 setter 和 getter。
pub struct Struct {
value: u32
}
pub trait Trait<T> {
fn method(&mut self, arg: T);
}
impl Trait<u32> for Struct {
fn method(&mut self, arg: u32) {
self.value = arg;
}
}
impl Trait<&mut u32> for Struct {
fn method(&mut self, arg: &mut u32) {
*arg = self.value;
}
}
此结构的可能用法如下
fn main() {
let mut s = Struct { value: 0 };
let mut v = 0;
s.method(&mut v);
println!("The value of s is: {}", v);
s.method(3);
s.method(&mut v);
println!("The value of s is: {}", v);
}
在 ffi 接口中使用结构时,调用重载方法而不是直接访问字段的优势有两个原因。一方面,该方法仍然可以对值进行修改,例如先将 &str
转换为 CString
然后将 *const u8
作为值存储,这使得字符串可用对于 C。另一方面,重载该方法还可以使用 C 中为其指定的名称,而不是编写 setValue
和 getValue
等。但是,正如您在此处看到的,这两种方法中的一种不需要对 self 的可变引用,因为它不会更改值字段,但由于特征需要它,所以无论如何都会在两种情况下使用它。该结构不仅被配置并用作 ffi 方法中的参数,而且还可以作为此类方法的 return 值出现,在这种情况下,它将被 returned 作为不可变引用并且应该只能从中读取。定制的特征实现看起来像这样
impl Trait<u32> for Struct {
fn method(&mut self, arg: u32) {
self.value = arg;
}
}
impl Trait<&mut u32> for Struct {
fn method(&self, arg: &mut u32) {
*arg = self.value;
}
}
显然这在这里不起作用,因为第二个 impl 块没有与特征相同的方法签名。我已经尝试使用 arbitrary_self_types
功能将 self 参数定义为特征中的另一个通用参数,但不幸的是,这没有用。
您不能对可变性进行参数化。
参见:
- internals.rust-lang.org - Parameterisation over mutability.
- internals.rust-lang.org - Generic mutability parameters.
你可以参数化self
,但它不再是一个方法,只是一个关联函数:
pub trait Trait<This, T> {
fn method(this: This, arg: T);
}
impl Trait<&mut Self, u32> for Struct {
fn method(this: &mut Self, arg: u32) {
this.value = arg;
}
}
impl Trait<&Self, &mut u32> for Struct {
fn method(this: &Self, arg: &mut u32) {
*arg = this.value;
}
}
fn main() {
let mut s = Struct { value: 0 };
let mut v = 0;
Struct::method(&s, &mut v);
println!("The value of s is: {}", v);
Struct::method(&mut s, 3);
Struct::method(&s, &mut v);
println!("The value of s is: {}", v);
}
鉴于 RFC 草案 Refined trait implementations 有可能 完善 其实现中的一个特征,即编写一个不太通用的 impl(例如 impl 中的安全方法这在特征中是不安全的),您将来也可能会改进可变性(尽管我还没有看到有关的讨论),但这只会在您使用具体实例时放宽限制,不适用于仿制药。
无论如何,我认为这种设计是不正确的。使用普通的value()
和set_value()
方法,简单明了。
给出的是一个带有 u32
字段的结构。它只有一个重载方法,可以同时用作字段的 setter 和 getter。
pub struct Struct {
value: u32
}
pub trait Trait<T> {
fn method(&mut self, arg: T);
}
impl Trait<u32> for Struct {
fn method(&mut self, arg: u32) {
self.value = arg;
}
}
impl Trait<&mut u32> for Struct {
fn method(&mut self, arg: &mut u32) {
*arg = self.value;
}
}
此结构的可能用法如下
fn main() {
let mut s = Struct { value: 0 };
let mut v = 0;
s.method(&mut v);
println!("The value of s is: {}", v);
s.method(3);
s.method(&mut v);
println!("The value of s is: {}", v);
}
在 ffi 接口中使用结构时,调用重载方法而不是直接访问字段的优势有两个原因。一方面,该方法仍然可以对值进行修改,例如先将 &str
转换为 CString
然后将 *const u8
作为值存储,这使得字符串可用对于 C。另一方面,重载该方法还可以使用 C 中为其指定的名称,而不是编写 setValue
和 getValue
等。但是,正如您在此处看到的,这两种方法中的一种不需要对 self 的可变引用,因为它不会更改值字段,但由于特征需要它,所以无论如何都会在两种情况下使用它。该结构不仅被配置并用作 ffi 方法中的参数,而且还可以作为此类方法的 return 值出现,在这种情况下,它将被 returned 作为不可变引用并且应该只能从中读取。定制的特征实现看起来像这样
impl Trait<u32> for Struct {
fn method(&mut self, arg: u32) {
self.value = arg;
}
}
impl Trait<&mut u32> for Struct {
fn method(&self, arg: &mut u32) {
*arg = self.value;
}
}
显然这在这里不起作用,因为第二个 impl 块没有与特征相同的方法签名。我已经尝试使用 arbitrary_self_types
功能将 self 参数定义为特征中的另一个通用参数,但不幸的是,这没有用。
您不能对可变性进行参数化。
参见:
- internals.rust-lang.org - Parameterisation over mutability.
- internals.rust-lang.org - Generic mutability parameters.
你可以参数化self
,但它不再是一个方法,只是一个关联函数:
pub trait Trait<This, T> {
fn method(this: This, arg: T);
}
impl Trait<&mut Self, u32> for Struct {
fn method(this: &mut Self, arg: u32) {
this.value = arg;
}
}
impl Trait<&Self, &mut u32> for Struct {
fn method(this: &Self, arg: &mut u32) {
*arg = this.value;
}
}
fn main() {
let mut s = Struct { value: 0 };
let mut v = 0;
Struct::method(&s, &mut v);
println!("The value of s is: {}", v);
Struct::method(&mut s, 3);
Struct::method(&s, &mut v);
println!("The value of s is: {}", v);
}
鉴于 RFC 草案 Refined trait implementations 有可能 完善 其实现中的一个特征,即编写一个不太通用的 impl(例如 impl 中的安全方法这在特征中是不安全的),您将来也可能会改进可变性(尽管我还没有看到有关的讨论),但这只会在您使用具体实例时放宽限制,不适用于仿制药。
无论如何,我认为这种设计是不正确的。使用普通的value()
和set_value()
方法,简单明了。