如何为自我的不同可变性实现特征

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 中为其指定的名称,而不是编写 setValuegetValue 等。但是,正如您在此处看到的,这两种方法中的一种不需要对 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 参数定义为特征中的另一个通用参数,但不幸的是,这没有用。

您不能对可变性进行参数化。

参见:

可以参数化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()方法,简单明了。