Using a trait-method from an Arc wrapped struct

Hello people,

I recently met a use case, where I will have to pass some handlers into a register function.

The handler api is defined as a trait implemented on a struct, so I can just pass-in the dedicated object.

I have created a dummy code to better illustrate this issue:

pub trait DisplayHandler {
    fn display_something(&self);
}

pub trait ErrorHandler {
    fn display_error(&self, error: &str);
}

fn register_test_trait<
    D: DisplayHandler + Send + 'static,
    E: ErrorHandler + Send + 'static,
> (
    display_handler: F,
    error_handler: E,
) {
    let _ = std::thread::spawn(move|| {
        loop {
            display_handler.display_something();
            std::thread::sleep(std::time::Duration::from_secs(2));
        }
    });

    let _ = std::thread::spawn(move|| {
        loop {
            error_handler.display_error("I am an error");
            std::thread::sleep(std::time::Duration::from_secs(5));
        }
    });
}

pub struct MyStruct {}

impl DisplayHandler for MyStruct {
    fn display_something(&self) {
        println!("Display handler: showing something");
    }
}

impl ErrorHandler for MyStruct {
    fn display_error(&self, error: &str) {
        println!("Error handler: {}", error);
    }
}

fn main() {
    let my_struct = MyStruct {};

    let my_struct = std::sync::Arc::new(my_struct);
    let my_struct_display = my_struct.clone();
    let my_struct_error = my_struct.clone();

    register_test_trait(my_struct_display, my_struct_error );

    loop {
        std::thread::sleep(std::time::Duration::from_millis(200));
    }
}

As you can see the two handler traits, DisplayHandler and ErrorHandler. This is the required API passing to register_test_trait function, where the two handlers could be used in the same thread or different.

What I did on my side is that I am trying to implement these two traits onto one struct, MuStruct. Then before I try to pass to the register function.

I try to wrap my_struct with Arc pointer and created two duplicated copy, and pass the copied struct in.

However, the compiler doesn't seem happy with this, it says:

error[E0277]: the trait bound `std::sync::Arc<MyStruct>: DisplayHandler` is not satisfied
  --> src/main.rs:52:5
   |
9  | fn register_test_trait<
   |    -------------------
10 |     D: DisplayHandler + Send + 'static,
   |        -------------- required by this bound in `register_test_trait`
...
52 |     register_test_trait(my_struct_display, my_struct_error );
   |     ^^^^^^^^^^^^^^^^^^^ the trait `DisplayHandler` is not implemented for `std::sync::Arc<MyStruct>`

error[E0277]: the trait bound `std::sync::Arc<MyStruct>: ErrorHandler` is not satisfied
  --> src/main.rs:52:5
   |
9  | fn register_test_trait<
   |    -------------------
10 |     D: DisplayHandler + Send + 'static,
11 |     E: ErrorHandler + Send + 'static,
   |        ------------ required by this bound in `register_test_trait`
...
52 |     register_test_trait(my_struct_display, my_struct_error );
   |     ^^^^^^^^^^^^^^^^^^^ the trait `ErrorHandler` is not implemented for `std::sync::Arc<MyStruct>`

Regarding to the official document: ** Arc<T> automatically dereferences to T (via the Deref trait), so you can call T 's methods on a value of type Arc<T>**, I shouldn't encounter this error?

Can anyone help me figure out what's going wrong?

Basically the issue is that Arc<MyStruct> is a different type from MyStruct, and only the latter implements the required traits.

As for the Deref trait, well all that does is automatically convert an &Arc<MyStruct> into an &MyStruct when the compiler determines you really wanted an &MyStruct. In this case there are no references involved, so the conversion does not apply.

2 Likes
  1. Coercions don't happen if there is type inference
  2. Deref coercion can only work if there are references involved

Since your code breaks both of these rules, it doesn't work.

You can fix it like so:

fn register_test_trait<
    D: DisplayHandler + Send + 'static,
    E: ErrorHandler + Send + 'static,
> (
    display_handler: &F,
    error_handler: &E,
) { ... }

The call it like

register_test_trait(&my_struct_display, &my_struct_error);

But this will still break rule 1, no coercions if there is inference. But we can solve this is a few ways

register_test_trait::<MyStruct, MyStruct>(&my_struct_display, &my_struct_error);

register_test_trait(&my_struct_display as &MyStruct, &my_struct_error as &MyStruct);

Or we can notice that we don't need Arc anymore!

2 Likes

Thanks for the replies.

However, in my situation, the register function is not the part under my control.

Meaning that I can only tweak MyStruct entity before passing in to register function.

You can implement the trait for Arc<MyStruct>

impl DisplayHandler for Arc<MyStruct> {
    fn display_something(&self) {
        println!("Display handler: showing something");
    }
}

impl ErrorHandler for Arc<MyStruct> {
    fn display_error(&self, error: &str) {
        println!("Error handler: {}", error);
    }
}
1 Like

This can be the solution for the dummy code cause those two traits are defined in the same crates with where I use it.

However, in the real case, I have the register function and two api traits defined in the external crates.

If you're running into trouble with the orphan rules, you can make a wrapper struct:

struct MyStructArc(Arc<MyStruct>);

impl DisplayHandler for MyStructArc {
    fn display_something(&self) {
        println!("Display handler: showing something");
    }
}

impl ErrorHandler for MyStructArc {
    fn display_error(&self, error: &str) {
        println!("Error handler: {}", error);
    }
}
2 Likes

It works indeed, thank you!

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.