leptos-rs / leptos

Build fast web applications with Rust.
https://leptos.dev
MIT License
15.91k stars 625 forks source link

Nested routes in separate component don't compile when already inside ParentRoute. #2996

Open zakstucke opened 4 days ago

zakstucke commented 4 days ago

Describe the bug Separating routes into a separate component works at the top level of <Routes />, but fails to compile when already inside a <ParentRoute/>.

Leptos Dependencies Verified on main branch.

To Reproduce The router example fails to compile when wrapping the <ContactRoutes/> component in an outer <ParentRoute/>. Simplified below:

Fails to compile.

#[component]
pub fn RouterExample() -> impl IntoView {
    view! {
        <Router>
            <Routes fallback=|| "404">
                <ParentRoute path={path!("/foo")} view={|| view! { <Outlet /> }}>                
                    <ContactRoutes/>
                </ParentRoute>
            </Routes>
        </Router>
    }
}

// You can define other routes in their own component.
// Routes implement the MatchNestedRoutes
#[component]
pub fn ContactRoutes() -> impl MatchNestedRoutes<Dom> + Clone {
    view! {
        <ParentRoute path=path!("") view={|| view! { <Outlet /> }}>
            <Route path=path!("/") view=|| "Select a contact."/>
        </ParentRoute>
    }
    .into_inner()
}

Compiles (ContactRoutes contents in place instead of separate component)

#[component]
pub fn RouterExample() -> impl IntoView {
    view! {
        <Router>
            <Routes fallback=|| "404">
                <ParentRoute path={path!("/foo")} view={|| view! { <Outlet /> }}>                
                    <ParentRoute path=path!("") view={|| view! { <Outlet /> }}>
                        <Route path=path!("/") view=|| "Select a contact."/>
                    </ParentRoute>
                </ParentRoute>
            </Routes>
        </Router>
    }
}

Error:

error[E0599]: the method `build` exists for struct `RoutesPropsBuilder<NestedRoute<(StaticSegment<&str>,), impl MatchNestedRoutes<Dom> + Clone, (), ..., ...>, ..., ..., ...>`, but its trait bounds were not satisfied
   --> src/lib.rs:22:5
    |
22  |       view! {
    |  _____^
23  | |         <Router>
24  | |             <Routes fallback=|| "404">
25  | |                 <ParentRoute path={path!("/foo")} view={|| view! { <Outlet /> }}>
...   |
29  | |         </Router>
30  | |     }
    | |_____^ method cannot be called due to unsatisfied trait bounds
    |
   ::: /Users/zak/z/code/leptos/router/src/components.rs:208:1
    |
208 |   #[component]
    |   ------------ doesn't satisfy `<_ as ReactiveFunction>::Output = _`, `_: ReactiveFunction` or `_: Render<_>`
    |
   ::: /Users/zak/z/code/leptos/router/src/matching/nested/mod.rs:24:1
    |
24  |   pub struct NestedRoute<Segments, Children, Data, View, R> {
    |   --------------------------------------------------------- doesn't satisfy `_: MatchNestedRoutes<Dom>`
gbj commented 3 days ago

This is an interesting case in which the compiler is able to infer that the types satisfy the trait constraints quite easily, but it's fairly hard to express to the compiler "trust me, this thing is going to satisfy those constraints" in an impl block. I played around with it for twenty or thirty minutes and... failed.

That said, that suggests to me that it's a design problem, not a user problem. I will mess around with things a little to see if I can make it easier to express to the compiler that this is fine.