How to define and call external function that returns 'impl Trait'?

Hi there,

is there a way to create a library crate that contains an external function declaration that returns a Trait object and can be used as impl Trait?

The following attempts failed so far:

pub trait Trait {
    type Type;

    fn foo(&self) -> Option<Self::Type>;
}

fn baz(fool: impl Trait<Type=()> + 'static + Send) {
    // do something...
}

fn main() {
   baz( unsafe { bar() } );
}

extern "Rust" {
    fn bar() -> impl Trait<Type=()>;
}
/// error[E0562]: `impl Trait` not allowed outside of function and inherent method return types

Well I do understand, why the compilation failes, as impl Trait is meant to allow the callee to provide the actual type. As the callee is only external available Rust cannot infer the actual type. But when I Box the result and make it a dyn Trait like so:

extern "Rust" {
    fn bar() -> Box<dyn Trait<Type=()> + 'static + Send>;
}

fn main() {
    baz(unsafe { *bar() });
}

The compiler complains that the size of the result of *bar() is not known at compile time which is also somewhat true. So is there any way to get the external call to defined in a way the actual type is known to the caller ?

The background to this question is, that I try to provide a library crate that is expecting a specific function to be implemented by the user of this crate that returns a trait object for additional initialization to be done in the library crate to make usage of the library crate as convenient as possible for its user.

Thanks in advance for any hint or suggestion.

You can't take a trait object (or any other unsized value) by value, at least for now. Why don't you just manipulate the returned trait object through its wrapping Box? That's the usual way to go.

Hi, well thanks for your response. I'm not using the dyn Trait at the moment as all other functions in my library crate currently uses the impl Trait as parameter, but I'll check if I can work around that and use the Boxed value / the dyn Trait directly.

Trait objects themselves satisfy the trait that they are wrapping (if they didn't, that would be extremely weird), so you should be able to pass &dyn Trait where &(impl Trait + ?Sized) is expected (playground).

Hey thanks again.
The thing is that I've a function in my trait that will Pin the trait object and therefore need to be Sized.
I've updated the playground to reflect that. Now the compiler complains about an unknown size on compile time :frowning:
Any further ideas?

Well the trait requires where Self: Sized to call that function, but you're calling it on a impl Foo + ?Sized, which might not be sized, so it's no surprise it complains.

Besides, the boxing function takes ownership of the value, but you only have a borrow, so you wouldn't be able to call it anyway.

1 Like

If you want to take stuff by value, you won't be able to get around it being Sized with any amount of trickery. If you want dynamism, you can't have sizedness. Either make the user-provided function return a concrete type, or deal with it being dynamically-sized.

Hey, thx again. That's not the answer I was hoping for, but need to deal with it :wink:
Thx for your support so far.

This leads a bit to the question of how someone could than write a library that requires some - lets say "input" from the using crate where the library would need to request this kind of data, so there is no "main" in the user crate that is calling a function of the library to pass the data, but again rather the library is requesting it and taking "ownership" of the data provided?

Callbacks are usually handled by asking the user to pass in a function rather than requiring them to implement one with a pre-defined name. Instead of having the downstream crate write an extern { fn bar() }, add a functional argument at the place just where you want to call it. In this manner you don't need unsafe and you can even use generics for more flexibility, e.g.:

fn func_that_needs_result_of_bar<T: Trait, F: FnOnce() -> T>(bar: F) {
    baz(bar());
}
1 Like

Hi,
thanks for this hint and I really like this approach.
However, please excuse my ignorance as I'm still not able to grasp how I can adopt it to my scenario. Maybe I've still some knot tied in my brain :open_mouth:

How would your callback approach being implemented across 2 crates?
Assuming we have 2 crates where one crate is the provider that comes with a simple but small "runtime" and requires the consumer crate to kind of register itself:

use std::pin::Pin;

pub trait Trait {
    type Type;

    fn foo(&self) -> Option<Self::Type>;
    fn boxing(self) -> Pin<Box<Self>> where Self: Sized {
        Box::pin(self)
    }
}

fn need_external_bar_result<T: Trait<Type=()> + Send, F: FnOnce() -> T>(bar: F) {
    baz( bar() );
}

fn baz(foo: impl Trait<Type=()> + Sized + Send) {
    // do something...
    let boxed_trait = foo.boxing();
}

/// only the 'provider' will have a 'main' function and already comes with the 'rutime'
/// the 'consumer' is requested to deliver some initial data the 'provider' can work with to be able
/// to interoperate with the 'consumer' further down the road through defined interfaces/traits 
fn main() {
    // what to call here that the external crate can provide ?
}

So, what need to be written in the consumer crate to allow the provider crate to call into it and request the initialization data it needs, but only can be provided by the consumer ?
It could also be, that what I want to achieve is nonsense and not possible, but I really wish for it to work somehow.

Thanks in advance for your time...

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