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

xilem_core: return element in View::rebuild #3

Merged

Conversation

Philipp-M
Copy link
Collaborator

So this resulted out of experimenting with a rewrite of xilem_web where I needed access to the element after wrapping views have used View::rebuild.
It was not possible to access the element anymore because of the move of element type ViewElement::Mut, so this (unfortunately further complexification) made this possible again by returning ViewElement::Mut. I've tested all the examples, and they all are running as expected.

Maybe this also helps with the reborrowing issue?

@DJMcNab
Copy link
Owner

DJMcNab commented Jun 2, 2024

It is very frustrating that there's no way to represent normal reborrowing in the type system.

I am however slightly surprised by this requirement - I would think that you be able to use &mut Something in xilem_web (given that's what the previous form used), and that should be trivially reborrowable.

I really don't like that this adds that required <'a> in all of the rebuild implementations...

@Philipp-M
Copy link
Collaborator Author

It is very frustrating that there's no way to represent normal reborrowing in the type system.

Indeed, having a generalized type/trait, that &mut T implements, but other user types would also be able to, would be awesome, like a generic borrow-checker type or something like that...

I really don't like that this adds that required <'a> in all of the rebuild implementations...

Yep I'm also not a big fan of it, but I don't know how we get around it otherwise, while having access to the element after View::rebuild other than removing the lifetime in ViewElement::Mut or even the whole type alltogether, but that requires changes in masonry, while AFAIK WidgetMut<'a> a core-idea of masonry, and as described below, I also benefit from that idea in my rewrite currently...

I would think that you be able to use &mut Something in xilem_web

Well, I started doing something similar as masonry with a Pod and PodMut with a Drop impl to apply changes, this makes the API of xilem_web cleaner and more flexible.

Anyway, when we want to do something similar in masonry what I'm currently doing in xilem_web, we need this anyway.
It gives implementors more control over the element on the down and up traversal of the view tree and in general also over the lifetime of ViewElement::Mut, which I think will generally be useful...

For example, when after a rebuild of a child view, an implementor decides to View::teardown the child based on changes of the element it can use the returned child element type.

And well for my concrete usecase currently, by having a modifier system, that reads attributes of the element after the child views have modified it.

@DJMcNab
Copy link
Owner

DJMcNab commented Jun 3, 2024

Is it at all possible for you to manually reborrow for this use case (e.g. either with the signature of the commented-out reborrow function, or just manually).

Without seeing the code which requires this, I'm extremely hesitant to add it - it makes the rebuild functions so much messier to write and use.

@Philipp-M
Copy link
Collaborator Author

I mean I can get around it with a lot of ugly interior mutability via something like Rc<RefCell<T>> . As I only need mutable access to the element before and after (but not while) View::rebuild, as this PR already hints, but I'm not sure it's worth it, as it makes the implementation more ugly/error-prone, possibly less performant and likely increases binary size, and my other points are still valid.

As already written on zulip: I think it's a question about whether we want to make the View trait flexible and somewhat complex (which I think we're already heading to with the generic View PR) or user-friendly but limited.

it makes the rebuild functions so much messier to write and use.

Other than the added boilerplate in the signature (which will mostly be autogenerated by rust-analyzer)? We could also return Option<ViewElement::Mut>, but I'm not sure whether that's better (as it may require .unwrap/expect()).

element: <Self::Element as ViewElement>::Mut<'_>,
) {
element: <Self::Element as ViewElement>::Mut<'a>,
) -> <Self::Element as ViewElement>::Mut<'a> {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we can add a xilem_core::Mut type alias, to remove the as ViewElement.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tested it with the following which does work:

pub type Mut<'a, E> = <E as ViewElement>::Mut<'a>;

it resolves to something like when using rust-analyzer and using it in the trait:

Suggested change
) -> <Self::Element as ViewElement>::Mut<'a> {
) -> xilem_core::Mut<'a, Self::Element> {

Should I add that type alias?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that would be good. I'd hope to remove the xilem_core:: as well by importing it.

If it were in a seperate PR, I'd just merge that very quickly; I'm still not completely sure I want this change, although now is the time to make it...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that's possible with the code-action to remove that xilem_core::, and when it's in scope (use xilem_core::Mut) it won't appear in the code-action to impl that View trait.

I get that hesitation, but I do think it's worth it, since it's basically all autogenerated boilerplate (via rust-analyzer) and the lifetime is somewhat trivial, but it adds additional flexibility (also in the main xilem implementation) by being able to modify the element after descendent views have modified it, which I think is quite useful for modifier based implementations that rely on previous modifications (while not having &mut E as ViewElement::Mut type).

…iewElement::Mut`

It's not possible to modify the element otherwise, after descendent `View`s have used the element in `View::rebuild`
@Philipp-M Philipp-M force-pushed the xilem_core-rebuild-return-element branch from 7f4faff to 62a7e05 Compare June 4, 2024 20:26
@Philipp-M
Copy link
Collaborator Author

Additionally to the type alias and rebase, I did some cleanup for all the <Self::Element as ViewElement>::Mut<'_> as well.

Copy link
Owner

@DJMcNab DJMcNab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some thoughts on code style and nits on documentation. But I think this direction is fine

xilem/src/view/button.rs Outdated Show resolved Hide resolved
xilem_core/src/lib.rs Outdated Show resolved Hide resolved
xilem_core/src/view.rs Show resolved Hide resolved
xilem_core/src/lib.rs Show resolved Hide resolved
@Philipp-M
Copy link
Collaborator Author

Thanks for the review, I'll merge

@Philipp-M Philipp-M merged commit 6fa0c55 into DJMcNab:core-two Jun 4, 2024
8 checks passed
@Philipp-M Philipp-M deleted the xilem_core-rebuild-return-element branch June 4, 2024 21:17
DJMcNab added a commit that referenced this pull request Nov 11, 2024
New tracy image:


![image](https://github.com/user-attachments/assets/94e54c89-8159-4dd4-a521-4a7122f64375)

New log tracing file:
<details><summary>An overview of the new logs</summary>
<p>

```
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}: masonry::passes::update: RootWidget received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Flex{id=linebender#8}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Flex{id=linebender#8}:SizedBox{id=linebender#7}: masonry::passes::update: SizedBox received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Flex{id=linebender#8}:SizedBox{id=linebender#7}:Flex{id=linebender#6}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Flex{id=linebender#8}:SizedBox{id=linebender#7}:Flex{id=linebender#6}:Flex{id=#3}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Flex{id=linebender#8}:SizedBox{id=linebender#7}:Flex{id=linebender#6}:Flex{id=#3}:Prose{id=#1}: masonry::passes::update: Prose received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Flex{id=linebender#8}:SizedBox{id=linebender#7}:Flex{id=linebender#6}:Flex{id=#3}:Label{id=#2}: masonry::passes::update: Label received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Flex{id=linebender#8}:SizedBox{id=linebender#7}:Flex{id=linebender#6}:Flex{id=linebender#5}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Flex{id=linebender#8}:SizedBox{id=linebender#7}:Flex{id=linebender#6}:Flex{id=linebender#5}:VariableLabel{id=#4}: masonry::passes::update: VariableLabel received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Flex{id=linebender#17}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Flex{id=linebender#17}:Button{id=linebender#10}: masonry::passes::update: Button received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Flex{id=linebender#17}:Button{id=linebender#10}:Label{id=linebender#9}: masonry::passes::update: Label received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Flex{id=linebender#17}:Button{id=linebender#12}: masonry::passes::update: Button received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Flex{id=linebender#17}:Button{id=linebender#12}:Label{id=linebender#11}: masonry::passes::update: Label received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Flex{id=linebender#17}:Button{id=linebender#14}: masonry::passes::update: Button received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Flex{id=linebender#17}:Button{id=linebender#14}:Label{id=linebender#13}: masonry::passes::update: Label received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Flex{id=linebender#17}:Button{id=linebender#16}: masonry::passes::update: Button received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Flex{id=linebender#17}:Button{id=linebender#16}:Label{id=linebender#15}: masonry::passes::update: Label received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Portal{id=linebender#163}: masonry::passes::update: Portal received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Portal{id=linebender#163}:Flex{id=linebender#160}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Portal{id=linebender#163}:Flex{id=linebender#160}:SizedBox{id=linebender#24}: masonry::passes::update: SizedBox received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Portal{id=linebender#163}:Flex{id=linebender#160}:SizedBox{id=linebender#24}:Flex{id=linebender#23}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Portal{id=linebender#163}:Flex{id=linebender#160}:SizedBox{id=linebender#24}:Flex{id=linebender#23}:Flex{id=linebender#20}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Portal{id=linebender#163}:Flex{id=linebender#160}:SizedBox{id=linebender#24}:Flex{id=linebender#23}:Flex{id=linebender#20}:Prose{id=linebender#18}: masonry::passes::update: Prose received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Portal{id=linebender#163}:Flex{id=linebender#160}:SizedBox{id=linebender#24}:Flex{id=linebender#23}:Flex{id=linebender#20}:Label{id=linebender#19}: masonry::passes::update: Label received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Portal{id=linebender#163}:Flex{id=linebender#160}:SizedBox{id=linebender#24}:Flex{id=linebender#23}:Flex{id=linebender#22}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Portal{id=linebender#163}:Flex{id=linebender#160}:SizedBox{id=linebender#24}:Flex{id=linebender#23}:Flex{id=linebender#22}:VariableLabel{id=linebender#21}: masonry::passes::update: VariableLabel received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=linebender#165}:Flex{id=linebender#164}:Portal{id=linebender#163}:Flex{id=linebender#160}:SizedBox{id=linebender#31}: masonry::passes::update: SizedBox received Update::WidgetAdded
```

</p>
</details> 

This was originally an experiment into caching spans, but I determined
that was non-viable due to the pass names.
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