#Passing closure inside another closure makes it `FnOne` instead of `Fn`

7 messages · Page 1 of 1 (latest)

reef pier
#

I have this small code snippet that I created with the leptos framework:

use leptos::*;

pub struct MarkdownClickEvent;

#[component]
pub fn Markdown<F: Fn(MarkdownClickEvent) + 'static>(
    cx: Scope,
    source: ReadSignal<String>,
    #[prop(optional)] 
    onclick: Option<F>,
    ) 
    -> impl IntoView {
    let onclick = onclick.unwrap();
    view! {cx,
        <div style="width:100%"> 
            {move || parse(&source.get(), &onclick)}
        </div>
    }
}

fn parse<F>(source: &str, onclick: &F) -> impl IntoView 
where F: Fn(MarkdownClickEvent) + 'static
{
    unimplemented!()
}
#

This code works, but I would like to avoid cloning source (source.get clones the signal)
So I changed the code to

use leptos::*;

pub struct MarkdownClickEvent;

#[component]
pub fn Markdown<F: Fn(MarkdownClickEvent) + 'static>(
    cx: Scope,
    source: ReadSignal<String>,
    #[prop(optional)] 
    onclick: Option<F>,
    ) 
    -> impl IntoView {
    let onclick = onclick.unwrap();
    view! {cx,
        <div style="width:100%"> 
            {move || source.with(|x| parase(x, &onclick))}
        </div>
    }
}

fn parse<F>(source: &str, onclick: &F) -> impl IntoView 
where F: Fn(MarkdownClickEvent) + 'static
{
    unimplemented!()
}

Now, I get a compile error
```error[E0525]: expected a closure that implements the Fn trait, but this closure only implements FnOnce
--> src/lib.rs:17:14
|
14 | / view! {cx,
15 | | <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css" integrity="sha384-3UiQGuEI4TTMaFmGIZumfRPtfKQ3trwQE2JgosJxCnGmQpL/lJdjpcHkaaFwHl...
16 | | <div style="width:100%">
17 | | {move || source.with(move |x| parse(x, &onclick))}
| | ^^^^^^^ ------- closure is FnOnce because it moves the variable onclick out of its environment
| | |
| | this closure implements FnOnce, not Fn
18 | | </div>
19 | | }
| | -
| | |
| |_____the requirement to implement Fn derives from here
| required by a bound introduced by this call


I don't see why adding a level of closures creates this compile error ...
tranquil inlet
#

the code you quoted isn't the code with the error

#

in the error, the inner closure is a move closure, which means it always moves all of the variables it captures when it is constructed

#

so, it moves onclick out of its environment, i.e.. the outer move || closure, which makes it impossible to call the outer closure more than once

#

the answer may be to remove the move, if it's okay for the inner closure to borrow things

#

or you may have to clone onclick so that the inner closure can have it