#impl Trait, Trait Objects, and Pain. Oh my!

5 messages · Page 1 of 1 (latest)

cold orbit
#

Oh boy, do I have a cursed Rust question today:

#[tracing::instrument(level = "info", fields(error), skip_all)]
pub fn render_route_with_context<IV>(
    options: LeptosOptions,
    paths: Vec<RouteListing>,
    uri: Uri,
    additional_context: impl Fn() + 'static + Clone + Send,
    app_fn: impl Fn() -> IV + Clone + Send + 'static,
) -> impl Fn(
    Request<Body>,
) -> Pin<
    Box<
        dyn Future<Output = Response<StreamBody<PinnedHtmlStream>>>
        + Send
        + 'static,
    >,
> + Clone
+ Send
+ 'static
    where
        IV: IntoView,
{
    // 1. Process route to match the values in routeListing
    let path = uri.path_and_query().unwrap().as_str();
    let full_path = format!("http://leptos.dev{path}");
    // 2. Find RouteListing in paths. This should probably be optimized, we probably don't want to
    // search for this every time
    let listing: &RouteListing = paths.iter().find_map(|r|
        if r.path() == path{
            Some(r)
        }
        else {
            None
        }
    ).unwrap();
    // 3. Match listing mode against known, and choose function
    match listing.mode() {
        SsrMode::OutOfOrder => {
            render_app_to_stream_with_context(
                LeptosOptions::from_ref(&options),
                additional_context.clone(),
                app_fn.clone(),
            )
        }
        SsrMode::PartiallyBlocked => {
            render_app_to_stream_with_context_and_replace_blocks(
                LeptosOptions::from_ref(&options),
                additional_context.clone(),
                app_fn.clone(),
                true
            )
        }
        SsrMode::InOrder => {
            render_app_to_stream_in_order_with_context(
                LeptosOptions::from_ref(&options),
                additional_context.clone(),
                app_fn.clone(),
            )
        }
        SsrMode::Async => {
            render_app_async_with_context(
                LeptosOptions::from_ref(&options),
                additional_context.clone(),
                app_fn.clone(),
            )
        }
    }
}```

I'm trying to write a function that runs one of these four functions. This version errors out, saying the match arms don't match because different calls to `impl Trait` produce different opaque types, and recommends switching to a Boxed trait object. If I make a boxed trait object, changing the return signature to 
```rust
-> Box<dyn Fn(
    Request<Body>,
) -> Pin<
    Box<
        dyn Future<Output = Response<StreamBody<PinnedHtmlStream>>>
        + Send
        + 'static,
    >,
> + Clone
+ Send
+ 'static>

and Boxing everything, it tells me that it can only use auto traits for trait objects, and I should make a new trait. If I make a new trait

trait NewTrait: std::ops::Fn(http::Request<hyper::Body>) -> Pin<
    Box<
        dyn Future<Output = Response<StreamBody<PinnedHtmlStream>>>
        + Send
        + 'static,
    >,
> + Clone{}

It tells me I can't make it into a trait object because it needs Self: Sized. For the life of me I can't figure out where that should go, and whether this whole thing is a trap

rugged remnant
#

You can't clone a trait object. But you can use Arc<dyn Fn instead of Box<dyn Fn, which is clonable.

#

However, another option is to return just one function.

#
let mode = listing.mode();
move |request| {
    match mode {
        SsrMode::OutOfOrder => {
            render_app_to_stream_with_context(
                LeptosOptions::from_ref(&options),
                additional_context.clone(),
                app_fn.clone(),
            )(request)
        }
#

This way, the function returns one closure type, which doesn't need to be boxed, and the dispatch is handled when the closure is called.