3

Given is a struct with a u32 field. It has exactly one overloaded method that can be used as both setter and getter for the field.

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;
    }
}

A possible use of this structure could be as follows

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);
}

The advantage of calling an overloaded method instead of accessing the field directly serves two reasons when the struct is used in a ffi interface. On the one hand, the method can still make modifications to the value, such as first converting a &str to a CString and then storing the *const u8 as the value, which makes the string usable for C. On the other hand, overloading the method also makes it possible to use the names given for it in C instead of writing setValue and getValue, for example. However, as you can see here, one of the two methods does not need a mutable reference to self because it does not change the value field, but because the trait requires it, it is used in both cases anyway. The struct is not only configured and then used as argument in a ffi method, but can also occur as return value of such a method, in which case it will be returned as a immutable reference and should only be read from. The customized trait implementations would look like this

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;
    }
}

Obviously this won't work here because the second impl block doesn't have the same method signature as the trait. I already tried to define the self paramter as another generic parameter in the trait using the arbitrary_self_types features but unfortunately that didn't work.

Goldenprime
  • 324
  • 1
  • 10
  • So what is your question? – Herohtar May 02 '22 at 14:56
  • Also, generics is not overloading. Rust doesn't have function overloading. – Herohtar May 02 '22 at 14:57
  • So the question is, how do I get this code to work: fn main() { let s = Struct { value: 0 }; let mut v = 0; s.method(&mut v); println!("The value of s is: {}", v); } without breaking fn main() { let mut s = Struct { value: 0 }; s.method(34); } – Goldenprime May 02 '22 at 14:58
  • If it is not overloading, what is the correct expression for a method in rust that has the same name but different implementations for different parameter sets? – Goldenprime May 02 '22 at 15:01
  • You don't. That's not possible in Rust. Even if it were, that seems like a terrible design. The proper way to use functions to access fields in Rust is getter/setter (typically `value()`/`set_value()`) – Herohtar May 02 '22 at 15:02
  • That's just generics. Overloading means you have the same method name, but different numbers of parameters. Generics allow you to have a method that takes parameters of different types, and may have different implementation based on the type. – Herohtar May 02 '22 at 15:06
  • This pattern seems like a very bad idea to me. Due to implicit coercions from `&mut` to `&` via deref rules, it is not always clear which type of reference you have, and that can be changed subtly, by code that is far away. When you implement a trait for reference types the implementations should always do "the same thing" or else you'll end up with bugs that will be hard to find. – Peter Hall May 02 '22 at 19:41

1 Answers1

3

You cannot parameterize over mutability.

See:

You can parameterize on self, but it will not be a method anymore, only an associated function:

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);
}

Given the draft RFC Refined trait implementations that poses the possibility to refine a trait in its implementation, i.e. to write a less generic impl (e.g. safe method in the impl that is unsafe in the trait), it may be possible that you will be able to refine mutability in the future too (although I haven't seen discussions about that), but that will only relax the restriction when you work with a concrete instance, not with generics.

One way or the other, I don't think this design is correct. Just use normal value() and set_value() methods, it is simple and obvious.

Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77