Get struct from downstream client

I have a trait and its default impl in my lib. But I would also like the ability of the downstream clients to impl this trait. Basically, if my lib can figure out (somehow I dont know it yet maybe features?) that this trait is impl by a client, then use it instead of default impl.

Is this possible? If so how? All my attempts have hit a dead-end. :frowning:

It isn't clear what you want here. If you define a trait and it is public, then anyone using your crate can implement it and override any methods that have defaults if they choose to do so. As long as your functions accept something that implements the trait, they shouldn't need to know whether they have the default or some other implementation. It may be easier to understand what you are trying to do with a small code example if you can provide one.

Im working on a GUI lib that has a hierarchy of Widgets. These Widget's are wrapped in a WidgetWrapper. A WidgetWrapper is responsible for propagating events down widget hierarchy. I want users to... sort of instrument WidgetWrappers (eg., to find out event traversal, measuring perf, etc.)

So I added a trait DebuggableWidgetWrapper and provided a default impl, DebuggableWidgetWrapperImpl of this trait. DebuggableWidgetWrapperImpl simply wraps a WidgetWrapper.

Now, instead of having WidgetWrappers..... I have DebuggableWidgetWrapperImpl that basically do nothing but indirection.

pub trait DebuggableWidgetWrapper {
    // some functions
}

struct DebuggableWidgetWrapperImpl  {
    inner_wrapper: WidgetWrapper
}

impl DebuggableWidgetWrapper for DebuggableWidgetWrapperImpl  {
    // impl functions. all they do is call inner_wrapper's functions. 
}

So now my code goes from

struct {
    wrapper: WidgetWrapper
}

//initialize somewhere
wrapper: WidgetWrapper::new(),
...

to

struct {
    wrapper: Box<dyn DebuggableWidgetWrapper>
}

//initialize somewhere
wrapper: Box::new(DebuggableWidgetWrapperImpl::new(WidgetWrapper::new())) <--- Allow others to inject their own impl

If lib users have impl DebuggableWidgetWrapper then I want

wrapper: Box::new(Custom1WrapperImpl::new(WidgetWrapper::new()))

where Custom1WrapperImpl impls DebuggableWidgetWrapper

If I understand what you have now, you can already do what you want. Although I don't understand why you need the intermediate DebuggableWidgetWrapperImpl struct (e.g.).

I may not have understood correctly though.

@quinedot WidgetSpinner cannot have a wrapper. WidgetSpinner is wrapped in a WidgetWrapper.
WidgetWrapper has some domain specific logic that cannot be changed.

Let's take an example of "left mouse down" event. This event will be handled first by WidgetWrapper which decides if event needs to be propagated to its wrapper WidgetSpinner. So WidgetWrapper looks something like this:

impl WidgetWrapper {
    pub fn handle_event(event: LeftMouseDown) {
        // check few things
        if (/* those things are true */) {
            widget_spinner.send_event_to_the_widget(event)
        }
    }    
}

Now Im adding another level of indirection with DebuggableWidgetWrapperImpl.

impl DebuggableWidgetWrapper for DebuggableWidgetWrapperImpl {
    pub fn handle_event(event: LeftMouseDown) { // trait function. Different than WidgetWrapper::handle_event()
        let start = Instant::now();
        wrapper.handle_event(event)
        let end = Instant::now();
        println!("LeftMouseDown was handled in {:?} mins", end - start);
    }
}

Im calling DebuggableWidgetWrapperImpl default impl because as you can see above, it calculates time to handle a given event.
Let's say lib users want to, eg. start/end a span for tracing. So they'll provide a custom wrapper impl. Let's call it TraceableEventWrapperImpl. It'll looks like:

impl DebuggableWidgetWrapper for TraceableEventWrapperImpl {
    pub fn handle_event(event: LeftMouseDown) { // trait function. Different than WidgetWrapper::handle_event()
        let start = tracing::start_span()
        wrapper.handle_event(event)
        let end = tracing::end_span();
    }
}

Another example. Let's say someone else wants to print even traversal hierarchy in console. So they can provide another custom wrapper impl. Let's call this PrintEventTraversalWrapperImpl. It'll look like:

impl DebuggableWidgetWrapper for PrintEventTraversalWrapperImpl {
    pub fn handle_event(event: LeftMouseDown) { // trait function. Different than WidgetWrapper::handle_event()
        println!("\t");
        wrapper.handle_event(event)
        println!("\b);
    }
}

As you can see having a DebuggableWidgetWrapper gives lib users ability to instrument widgets. They can even instrument widgets they build not just out-fo-the-box widgets.

I hope this all makes sense. Honestly Im finding it difficult to even explain. Feel free to correct me or ask more questions.

It looks there like your just saying that a user could implement DebuggableWidgetWrapper and you want to use the type it is implemented on. If you make your API generic over T : DebuggableWidgetWrapper then it can be called with any type that implements it.

If your API also needs to do the wrapping, then you may need to take in a function to do the wrapping. That could be done with a trait function:

pub trait DebuggableWidgetWrapper {
    // some functions

    fn wrap(inner : WidgetWrapper) -> Self where Self : Sized;
}

The Self : Sized bound ensures you still have an object safe trait after the initial wrapping, but if you are wrapping everything in the same implementation, you may not need trait objects.

I kid you not, I had a dream last night on how I might be able to solve this. The GUI lib has certain functions that users can call that affect how widgets behave. For eg. when users start their app they can apply certain properties to the window and internally those properties are applied to all widgets. Like:

window.show_borders(Color::RED)

Above will show a window with widgets with red border.

On similar lines I can do:

window.show_borders(Color::RED).use_custom_wrapper_impl(Box::new(custom_type_that_impls_wdebuggable_wrapper));

And use_custom_wrapper_impl will be like

fn use_custom_wrapper_impl(type: Box<&dyn DebuggableWidgetWrapper>)

So now all my widgets will use the user provided impl of DebuggableWidgetWrapper

If users dont call use_custom_wrapper_impl then I'll use my internal debuggable wrapper impl.

Thanks for the reply. I think I found the solution in my dream. :rofl:

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.