What is the `diff` method in the Widget trait?

Hey! I’m new to Iced, and admittedly reading code in general, so apologies if there was some method that I was able to learn this without asking for help.

Anyways, I was wondering what the diff method in Widget is for? Looking at a random selection of examples and common widgets, most of them don’t seem to implement it at all. I was able to find one example use case in the toast example. There is a concept of the “old” and “new” (current?) state and it seems to arbitrarily modify the new state to somewhat match the old (matching the lengths of the two vectors, with no regards for their contents), though I can’t imagine why.

The doc comments mention “Reconciling the widget with the tree” though I also don’t understand why we might need to do this, or what reconciling in this case means.

I am currently learning about the API too and take some notes for expanding my guide.
I have not create a real custom widget yet and can not confirm that the following information is true. I researched it by looking at code and asking questions on the discord.

my notes so far on the diff method

This function compares/reconciles the old state Tree, with what you would expect it to be. If there should be something different, you change it in the given tree.

Often for widgets that contain other widgets it looks similar to this:

fn diff(&self, tree: &mut Tree) {
	tree.diff_children(&self.children);
}

It is useful to customize this when your state needs to be changed depending on the arguments of the new function. You might pass data into your new function which needs to be in the state of your widget.
Although this seems not to be the case that often.

In the research of this function I could not find a good example where this is clearly used and good to understand

Thanks for the reply! This and the guide were useful in helping me piece together more of the puzzle. After digging some more through the code, this is my current understanding (which take it with a grain of salt, since I’m relatively new to digging through code, and also have not confirmed anything with the authors):

  • diff is recursively called on the state tree every view call.
    • We can see this in the default diff implementation, which just calls diff on all of the widget’s children.
  • Each time view is called, our application stores a cache of the widget state tree on the previous call, as well as generates a new one.
  • We then call diff on any widget that has the same tag between the old and new trees.
    • I loosely understand tags as a unique id tied to the state of the widget state() though this is not necessarily true for all widgets (we’ll see this in the toast example).

    • It seems to me based on naming and it’s usage that the idea of the diff function is that we only need to update the parts of the tree that need updating, while we simply reuse the cached parts of the tree that remain the same between draw calls.

    • If the tag has changed, then we need to reconstruct the tree (not call diff).

    • If the tag is the same, the underlying widget is presumably the same, and so we can call the widget specific diff function to work out how to match the old tree with the new one, possible making the minimal amount of changes required.

In the toast example, we “abuse” the tag and diff functions a little in order to make toasts automatically disappear after a set amount of time.

  • The tag is defined as follows:
    fn tag(&self) -> widget::tree::Tag {
        struct Marker;
        widget::tree::Tag::of::<Marker>()
    }
    
    • There is a empty struct, and we call Tag::of on it. The tag of all empty structs will be the same.
      • In other words, diff will always be called on this function.
  • The Manager struct stores a vector of Toast’s
    • However the widget state stores a corresponding vector of Instant’s, which represent the time each toast was renewed.
  • In the diff method, we update a vector of Instant’s to match what is in the vector of Toast’s
    • The actual method of matching seems a little questionable to me.
    • When toasts expire, we remove instances starting from the most recently added ones. But I believe this is just a mistake.
  • The vector of Instant’s in the widget state is then used in the overlay function in order to work out how to display the toasts.

In other words, the diff function is not actually used as a way of updating the minimal amount of state required to create the new widget tree, but rather is used as a way to store logic that is called every time view is called, and which is used to update the widget state.

If people are interested, I’ve tried putting together a proposal for how I think we can implement an offiical toast widget. Fingers crossed it gets accepted!