Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add owning memos to allow memos that re-use the previous value #2139

Merged
merged 6 commits into from
Feb 20, 2024
Merged

feat: Add owning memos to allow memos that re-use the previous value #2139

merged 6 commits into from
Feb 20, 2024

Conversation

pheki
Copy link
Contributor

@pheki pheki commented Dec 29, 2023

Currently the memo function:

  • Only receives a reference to the previous value when running
  • Is required to return an owned value, even if it didn't change

This means that:

  • The returned type needs to be allocated and copied one time per source signal change, something that may be very common for slices

This PR implements a Proof Of Concept proposing a new, more primitive, "raw memo", that allows the memo function to receive the previous value as owned, allowing it to be re-used by the memo, like effects (in fact, part of the implementation was taken from there).

"Normal" memos were changed to be implemented by using the raw memo, so there's no need to create a new reactive node. It should be backwards compatible. I haven't checked if there's any performance / binary size impact (benchmarks seems to be broken).

Unfortunately, I don't think the create_slice functions can take advantage of the raw memos with their current signature, but the slice! macro should be able to.

Related discussion: #1034 (reply in thread)

Main use-case: slices

The most prominent use-case is for slices[1][2][3], which can then be implemented to only clone if the value has changed. In [1], being forced to clone on every "source signal" change is actually noted as a major drawback. (1 2 3)

You can use to only clone when the value has changed:

let token = create_raw_memo(move |old_token| {
    state.with(move |state| {
        if let Some(token) = old_token.filter(|old_token| old_token == &state.token) {
            (token, false)
        } else {
            (state.token.clone(), true)
        }
    })
});

You can even re-use the allocation:

let token = create_raw_memo(move |old_token| {
    state.with(move |state| {
        let is_different = old_token.as_ref() != Some(&state.token);
        let mut token = old_token.unwrap_or_else(String::new);

        if is_different {
            token.clone_from(&state.token);
        }
        (token, is_different)
    })
});

@gbj
Copy link
Collaborator

gbj commented Dec 30, 2023

This seems good! Might I suggest "owning" memo or something instead of "raw"?

Re: defensively taking the BorrowMut twice: It seems to me that the only situation in which that could happen would be if something during the memo function caused the memo itself to rerun, right? i.e., it would have to do something like setting a signal inside it, that would trigger an effect, that would read from the memo, which will still be marked Check at that point so will rerun?

In this case .take()-ing the value out of the Memo will also cause a panic because Memo::with() always runs the memo function to generate a value if it doesn't exist, so this would still panic.

That is just off the top of my head, I don't have a good test case for either of those situations. In any case it is 100% a bug in the user's code so I wouldn't worry about it too much.

@pheki
Copy link
Contributor Author

pheki commented Jan 13, 2024

This seems good! Might I suggest "owning" memo or something instead of "raw"?

Yes! A lot better :)

Re: defensively taking the BorrowMut twice: ...

Oh, sorry I didn't make it clear, I was gauging interest so I mostly copied that from the effect implementation (introduced on faa4dc4) and assumed it would be similar for Memos. From you explanation it looks like it's not, so I simplified it.

The test was also mostly adapted from leptos_reactive/tests/slice.rs, but I now updated to match the use-case better.

Also added some docs, I think it's ready for review now!

@pheki pheki marked this pull request as ready for review January 13, 2024 20:28
@pheki pheki changed the title feat: POC implementing a "raw" memo to allow memos that re-use the previous value feat: Add owning memos to allow memos that re-use the previous value Jan 13, 2024
@pheki
Copy link
Contributor Author

pheki commented Feb 19, 2024

Hi! Any feedback for this?

@gbj
Copy link
Collaborator

gbj commented Feb 20, 2024

So sorry for the delay. This dropped off my radar after I looked at it the last time. It looks great now! Thanks for your work.

@gbj gbj merged commit 0770b87 into leptos-rs:main Feb 20, 2024
@pheki pheki deleted the raw-memo branch February 20, 2024 02:39
@pheki
Copy link
Contributor Author

pheki commented Feb 20, 2024

ty!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants