Box: conversion from closure


#1

Hello!

I am really need to do next:

    let x: Box<Fn(i32) -> i32> = (|x| x + 1).into();

But it cannot be compiled!

My be there are some another way to do it?
Since I can do this

    let y: Box<Fn(i32) -> i32> = Box::new(|x| x + 1);

#2

Can you explain why you need the into() based way?


#3

Hi, vitalyd. I am trying to create view! macro to convert templates like:

    ...
    fn render(&self) -> impl Renderable {
        view! {
            <Panel id="main">
                <Button class="master" click={|e| Msg::Close}>"Close"</Button>
            </Panel>
        }
    }

into code like:

    let t0 = Text::new("Close");

    let mut p0_props = <Button as Component>::Props::default();
    p0_props.class = "master".into();
    p0_props.click = {|e| Msg::Close}.into();
    let p0 = Button::new(p0_props, [t0]);

    let mut p1_props = <Panel as Component>::Props::default();
    p1_props.id = "main".into();
    let p1 = Panel::new(p1_props, [p0]);

#4

Ok, so you want to plop the template text verbatim into the macro expansion.

Do you need closures? Are you expecting to allow capturing something from the environment in them? Cause if not, you can coerce the closure to a fn, e.g:

let x: fn(i32) -> i32 = |x| x + 1;

#5

in general it should be possible to grab environment variables, since it will be some sort of framework for ui applications (like yew or may be I’ll suggest such way for yew and relm)


#6

Ok. And is there a hard requirement to use boxed closures? If you were to use generics, then it should just work. Any reason Props can’t be, e.g.:

struct Props<F: FnMut() + 'static> {
     click: F, // or maybe Option<F> to allow `Default`
}

#7

Yes, I thought about this approach, but it may be possible if Rust finally got GAT working.


#8

Why does this need GAT support?


#9

Ok, how I see it.

First I need associate properties and message types within component structure, I decided to use trait with this types inside:


triat Component {
    type Props: Default;
    type Msg: Default;
    ... // there are also update and change methods assiciated
}

I need to have ButtonProps, and ButtonMsg to be able implement this trait:

struct ButtonProps {
    // some props related to button
}

enum ButtonMsgs {
    // some actions related to button
}

Then I need to implement this trait to the struct:

struct Button {
    // some data asossiated with Button component
}

impl Button {
    fn new(props: ButtonProps) {
        // impl 
    }
}

impl Component for Button {
    type Props = ButtonProps;
    type Msg = ButtonMsgs;

    // other implementations
}

… later after tempalte will be desugared it will be like:

...

let mut p1_props = <Button as Component>::Props::default();
p1_props.click = {|e| Msg::Click(e)}.into();
let p1 = Button::new(p1_props);

As you can see I can add generics to ButtonProps I will be forced to add to Button also.


#10

Ok, I see what you mean. The generics would be required but I was thinking type inference will mostly let you write the template code without filling them in manually. So something like:

trait Component {
    type Props: Default;
    type Msg;
}

struct ButtonProps<F> {
    click: Option<F>,
}

impl<F> Default for ButtonProps<F> {
    fn default() -> Self {
        Self { click: None }
    }
}

struct Button<F> {
    props: ButtonProps<F>,
}

impl<F: FnMut(&ButtonMsgs) + 'static> Component for Button<F> {
    type Props = ButtonProps<F>;
    type Msg = ButtonMsgs;
}

enum ButtonMsgs {
    A,
    B,
    C,
}

fn main() {
    let mut props = <Button<_> as Component>::Props::default();
    props.click = Some(|e: &_| println!("clicked"));
    let btn = Button { props };
}

#11

Finally I found an answer. But since no way to implement trait on standart types and there is some troubles with default (specialization feature) I decided to create onw MyInto trait:

#![feature(unsize, specialization)]

use std::marker::Unsize;
use std::borrow::Cow;

pub trait MyFrom<T>: Sized {
    fn my_from(_: T) -> Self;
}

pub trait MyInto<T>: Sized {
    fn my_into(self) -> T;
}

impl<T, U: MyFrom<T>> MyInto<U> for T 
{
    fn my_into(self) -> U {
        U::my_from(self)
    }
}

impl<T: Unsize<U>, U: ?Sized> MyFrom<T> for Box<U> {
    fn my_from(val: T) -> Box<U> {
        Box::new(val) as Box<U>
    }
}

impl<T, X: MyInto<T>> MyFrom<X> for Option<T> {
    fn my_from(t: X) -> Option<T> {
        let tmp: T = t.my_into();
        tmp.into()
    }
}

impl<'a> MyFrom<&'a str> for Cow<'a, str> {
    fn my_from(t: &'a str) -> Cow<'a, str> {
        t.into()
    }
}

impl<'a> MyFrom<String> for Cow<'a, str> {
    fn my_from(t: String) -> Cow<'a, str> {
        t.into()
    }
}

macro_rules! simple_impl {
    ($from: ty) => {
        impl MyFrom<$from> for $from {
            fn my_from(t: $from) -> $from {
                t
            }
        }
    }
}

simple_impl!(i64);
simple_impl!(u64);
simple_impl!(u32);
simple_impl!(i32);
simple_impl!(u16);
simple_impl!(i16);
simple_impl!(u8);
simple_impl!(i8);
simple_impl!(bool);
simple_impl!(String);

fn main() {
    let x: Option<Box<Fn(u32) -> u32>> = {|x|x}.my_into();
    let y: Option<Cow<'static, str>> = "Test".my_into();
    let y: Option<Cow<'static, str>> = "Test".to_string().my_into();
    let y: Option<u32> = 5.my_into();
    
    println!("{:?}", x.unwrap()(12));
}

#12

I would like to push it to upstream, but I still has some troubles with it.

impl<U: ?Sized, T: Unsize<U>> MyFrom<T> for Box<U> {
    fn my_from(val: T) -> Box<U> {
        Box::new(val) as Box<U>
    }
}

and it’s works, but it does not allow me write something like this:

impl<T/*: ???*/> MyFrom<T> for T {
    fn my_from(t: T) -> T { t }
}

because T may be Box too, but there is one implementation for that:
so I has

Box<Trait, T: Trait> -> Box<dyn Trait> // let x: Box<dyn Fn(u32) -> u32> {|x|x}.into();

and I am also need

Box<T> -> Box<T> // let x: Box<u32> = 5.into();

#13

Yeah, overlap is annoying :slight_smile:. One could make the argument that specialization should allow for this, given that Box<T> is more specific than T, but alas they’re considered equally (non)specific.

I thought you might be able to hack the blanket impl by bounding T: Copy, to at least cover some types, but this fails to compile on the premise that downstream crates can implement Copy for Box<_>. That’s of course completely bogus as Box will never be Copy, so perhaps that’s a bug.