Lens

In the previous sections we demonstrated binding a label to some data in a Model and used events to mutate the model and the text of the label updated automatically. This all seems to be possible simply by calling the bind method on a widget, so what exactly does this do, and what are its arguments?

For the basic example the binding looked like this:

.bind(AppData::value, |value| value.to_string())

The first argument to this method is a Lens. You can think of a lens as a function which takes some data as input and returns a piece of that data as output, usually as references. For example, what we need for the basic example is a function which takes an AppData and returns the value, an i32.

But the lens in the bind method above doesn't look like a function. So what's going on here?

This is where the #[derive(Lens)] macro comes in. There is a function but it's within a trait called Lens which looks like this:

pub trait Lens {
    type Source;
    type Target;

    fn view<'a>(&self, data: &'a Self::Source) -> &'a Self::Target;
}

The derive macro creates for us a zero-sized static type and then implments the Lens trait, which might look something like this:

pub struct SomeGeneratedType;

impl Lens for SomeGeneratedType {
    type Source = AppData;
    type Target = i32;

    fn view<'a>(&self, data: &'a AppData) -> &'a i32 {
        &data.value
    }
}

The other thing that the derive macro does is to create a static instance of the generated type, with the same name as the field (value), within a module called AppData. This is what allows us to use AppData::value to refer to the lens.

The second argument to the bind method is a converter closure which has as input the target type of the lens, in this case a reference to an i32 value, and has as output the expected input of the label, in this case an owned String. Therefore, to convert between the two types we use the .to_string() method on the value.