Traits that differ only in mutability of self param?

I have two traits that differ only in the mutability of the self param. As a way to kind of “merge” them, so that I can put either one in, say, a Vec<Box<dyn Foo>>, I wrote a blanket impl.

It runs, but this is not something I’ve done before. So I wanted to check - does this look okay? Or is there a better way?

pub trait Foo {
    fn bar(&mut self);
}
pub trait FooImmutable {
    fn bar(&self);
}

impl Foo for i32 {
    fn bar(&mut self) {
        println!("Foo::bar for i32");
    }
}
impl FooImmutable for f32 {
    fn bar(&self) {
        println!("FooImmutable::bar for f32");
    }
}
impl<T: FooImmutable> Foo for T {
    fn bar(&mut self) {
        println!("Blacket impl");
        FooImmutable::bar(self);
    }
}
fn main() {
    let i: i32 = 123;
    let f: f32 = 1.2;
    let mut foos: Vec<Box<dyn Foo>> = vec![];
    foos.push(Box::new(i));
    foos.push(Box::new(f));
    for foo in &mut foos {
        foo.bar();
    }
}

It really depends on your use case; for example, perhaps it would be best to have both methods on one trait, or maybe you don't even need separate methods, or maybe you shouldn't be using dynamic dispatch in the first place. The more context you give, the more help we can give.

1 Like

The types of references aren't just two flavors of mutability.

The &mut references are strictly exclusive and have much more restrictive lifetimes. There are situations where they're impossible to use or getting them requires having expensive locks.

When a function takes &mut it requires all of the restrictions immediately, whether it uses them or not.

Therefore, mapping &mut to & has limited usefulness, because it will impose all of the limitations and restrictions, without actually needing them, and without ability to even use them after converting to a shared &.

So you really need to decide whether you want shared access or want to forbid sharing. Mapping one to another gives you worst of both worlds.


When you do have two traits for either kind of reference, check out how Deref and DerefMut are related. They need separate impls, because making Deref require &mut in a blanket impl would make it impossible to implement it on immutable types.

1 Like