#Cloning a closure using a trait

48 messages · Page 1 of 1 (latest)

rugged canyon
#

?play

#[derive(Clone)]
struct Position;

struct MouseEvent;
struct Event(Position, MouseEvent);

trait Context {
    fn onclick(&self, e: Event);
}

pub fn make_callback<C: Context>(context: &C, position: &Position) 
    -> impl Fn(MouseEvent)
{
    let position = position.to_owned();
    move |x| {
        let event = Event (position.clone(), x);
        context.onclick(event)
    }
}
meager heathBOT
#
error[E0700]: hidden type for `impl Fn(MouseEvent)` captures lifetime that does not appear in bounds
  --> src/main.rs:16:5
   |
12 |   pub fn make_callback<C: Context>(context: &C, position: &Position) 
   |                                             -- hidden type `[closure@src/main.rs:16:5: 16:13]` captures the anonymous lifetime defined here
13 |       -> impl Fn(MouseEvent)
   |          ------------------- opaque type defined here
...
16 | /     move |x| {
17 | |         let event = Event (position.clone(), x);
18 | |         context.onclick(event)
19 | |     }
   | |_____^
   |
help: to declare that `impl Fn(MouseEvent)` captures `'_`, you can introduce a named lifetime parameter `'a`
   |
12 ~ pub fn make_callback<C: Context><'a>(context: &'a C, position: &'a Position) 
13 ~     -> impl Fn(MouseEvent) + 'a 
   |

For more information about this error, try `rustc --explain E0700`.```
rugged canyon
#

Here, Context is a trait that represents structs owning a closure

#

And I would like to use this closure to create a callback in make_callback

loud saffron
#

have you considered following the help note in the compiler error

rugged canyon
#

The thing is, I'm pretty sure that fixing the error will not solve my problem

#

In my mind, the thing returned by make_callback should live forever

#

So restraining the lifetime will break other parts of my code

loud saffron
#

well first, why are you taking references

#

if it was meant to allowed to live forever

rugged canyon
#

I wanted to answer "because I want to call it multiple times with the same context" but it's not a good point: if this function needs ownership, I need to make that explicit

loud saffron
#

?play

#[derive(Clone)]
struct Position;

struct MouseEvent;
struct Event(Position, MouseEvent);

trait Context {
    fn onclick(&self, e: Event);
}

fn make_callback<C: Context>(context: C, position: Position) 
    -> impl Fn(MouseEvent)
{
    move |x| {
        let event = Event (position.clone(), x);
        context.onclick(event)
    }
}
meager heathBOT
loud saffron
#

🤷‍♂️

rugged canyon
#

Right
I guess this post was a bit of a X-Y problem
Let me think about the question I really wanted to ask

topaz dune
#

Do you wanna reuse the same Context between different callbacks?

rugged canyon
#

Yes exactly
In fact in practise my Context is a struct that look like that:

struct ContextStruct<A,B,...Z> {
    onclick: A,
    ...
    other_function: Z
}
topaz dune
#

You can use Rc for that perhaps - just passing the context as an Rc<C> instead of &C

#

?play

use std::rc::Rc;

#[derive(Clone)]
struct Position;

struct MouseEvent;
struct Event(Position, MouseEvent);

trait Context {
    fn onclick(&self, e: Event);
}

fn make_callback<C: Context>(context: Rc<C>, position: Position) 
    -> impl Fn(MouseEvent)
{
    move |x| {
        let event = Event (position.clone(), x);
        context.onclick(event)
    }
}
meager heathBOT
rugged canyon
loud saffron
#

well if your onclick method mutates itself then you must use Rc<RefCell<C>>

#

but otherwise effectively yes its the same

rugged canyon
#

Ok I have a few questions to make sure there is not a simpler way in my specific case

rugged canyon
#

But maybe it should be better do do something like

struct ContextStruct<A,B,...Z> {
    onclick: Rc<A>,
    ...
    other_function: Rc<Z>
}
loud saffron
#

i'd question why you have that many parameters

rugged canyon
#

It's function traits

#

like A: Fn(MouseEvent)

#

and so on

loud saffron
#

eh if you are going to wrap it in an Rc for each generic i'd just use rc trait objects instead

#

Rc<dyn Fn(MouseEvent)>

#

and get rid of the generics

rugged canyon
#

At the end it may be better like that sure
I never know what to choose between trait parameters and boxed trait objects

#

So I wanted to keep generic functions all the way down

#

but that means I had to specify my trait bounds on each function ... a pain

rugged canyon
loud saffron
#

well Rc would be infeasible, unless you do Fn(A) -> Box<dyn View>

rugged canyon
#

I have this struct:

/// all the context needed to render markdown:
/// - `syntax_set`, `theme`, `onclick`, `render_links`, `katex_opts`
pub struct RenderContext {
    cx: Scope,

    /// syntax used for syntax highlighting
    syntax_set: SyntaxSet,

    /// theme used for syntax highlighting
    theme: Theme,

    /// callback to add interactivity to the rendered markdown
    onclick: Option<Rc<dyn Fn(MarkdownMouseEvent)>>,

    /// callback used to render links
    render_links: Option<Rc<dyn Fn(Scope, mdast::Link) -> 
        Box<HtmlElement<dyn ElementDescriptor>>>>,
}
#

I get this error:

#

error[E0277]: the trait bound `(dyn ElementDescriptor + 'static): ElementDescriptorBounds` is not satisfied
   --> src/render.rs:57:19
    |
57  |       render_links: Option<Rc<dyn Fn(Scope, mdast::Link) ->
    |  ___________________^
58  | |         Box<HtmlElement<dyn ElementDescriptor>>>>,
    | |_________________________________________________^ the trait `ElementDescriptorBounds` is not implemented for `(dyn ElementDescriptor + 'static)`
    |
note: required by a bound in `leptos::HtmlElement`
   --> /home/rambip/.cargo/registry/src/index.crates.io-6f17d22bba15001f/leptos_dom-0.4.6/src/html.rs:288:32
    |
288 |     pub struct HtmlElement<El: ElementDescriptor> {
    |                                ^^^^^^^^^^^^^^^^^ required by this bound in `HtmlElement`

loud saffron
#

HtmlElement<Box<dyn ElementDescriptor>> maybe

rugged canyon
#

almost same error

loud saffron
#

is that trait yours

rugged canyon
#

no it's from the leptos crate

rugged canyon
#

Ok I got around the problem