Promblem when using dyn object with `Arc::get_mut`

I found that I can't tuning a dyn object when I need to pass it to a function which takes a &mut Arc, here's an example:

use std::sync::Arc;

trait Foo {}

struct Bar {}

impl Foo for Bar {}

fn f1(foo: &mut Arc<dyn Foo>) {
    Arc::get_mut(foo).unwrap();
}

fn f2(bar: &Arc<Bar>) {}

fn main() {
    let mut bar = Arc::new(Bar {});
    // f1 f2 order matters.
    f1(&mut bar);
    f2(&bar)
}

This code will not compile because &mut bar cannot cast to &mut Arc<dyn Foo>, but I can't cast it when creating:

let mut bar: Arc<dyn Foo> = Arc::new(Bar {});

If I do this, the f2 line will cause a problem, since I can't cast &Arcas&Arc`.

What can I do to make the code compile?

P.S. Why I don't clone? The f1 function use Arc::get_mut and if I cloned, f1 will fail.

In general f1 could do stuff like *foo = Arc::new(SomeOtherTypeThatImplsFoo::new()), so you can no longer rely on the previous type of bar after f1 is called. An alternative would be for f1 to take &mut dyn Foo, then you would be able to call f1 with f1(bar.get_mut().unwrap())

1 Like

Let's say we can't change f1, any way to make it compile? Currently, I use unsafe codes, but I don't like it.

P.S. In the real case, f1 do have a reason to take a &mut Arc instead of &mut dyn Foo.

P.P.S I found unsafe code does not work either. Arc<dyn Foo> and Arc<Bar> have different size, first time to know..

Nah, that's not the problem. The problem is that unsized coercions can't be transitive, regardless of mutability. A pointer-to-pointer-to-X can't be treated as a pointer-to-pointer-to-Y even if &(mut) X can be coerced to &(mut) Y. (For example, pointer arithmetic doesn't work out. The equivalent is true in languages with classical object-oriented subtyping.)

@OP: if you are using unsafe for circumventing the type system or the borrow checker, then your code is probably unsound. Do this instead.

In general, there's usually no reason for taking &mut Arc directly, and even less reason for &Arc. If your function needs a copy of the Arc for ownership, then make it take an Arc by-value, preventing unnecessary copies. If you only need a reference, then accept a reference, not an Arc.

1 Like

Do this instead.

As I mentioned above, I can't change f1.

In general, there's usually no reason for taking &mut Arc directly, and even less reason for &Arc. If your function needs a copy of the Arc for ownership, then make it take an Arc by-value, preventing unnecessary copies. If you only need a reference, then accept a reference, not an Arc.

I slightly disagree with this, I can give examples, but this may a little off-topic here, I do want a solution in this certain case.

You can't convert a &Arc<T> to a &Arc<dyn Trait> directly. You can clone the &Arc<T> and coerce the clone, like this.

I mentioned this above, if I cloned, the Arc::get_mut(foo).unwrap(); in f1 will fail, since there are two instances holding it. That's why this question is tricky to me.

The reason f1 takes &mut Arc is that:

  1. it wants to get mut from it,
  2. then the arc will be cloned and passed to others.

It may look a little weird, but in the real case f1 is designed to be this for a reason.

This could be solved if you could change f1 to take a generic rather than a trait object. The problem is that once you go to a trait object, there's no going back. (Downcasting is only possible on dyn Any, but you can't coerce dyn Foo to dyn Any).

I don't think there is a direct solution. I strongly suggest redesigning your code.

1 Like

Anything stopping you from wrapping Arc<Bar> with something that implements Foo and forwards the calls to the Arc<Bar>?

code

2 Likes

That's practicable, though it needs a lot of code refactors. I assumed it would be a way in the language to manipulate dyn types safely, but it seems that it not as flexible as I thought.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.