-
Notifications
You must be signed in to change notification settings - Fork 87
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
style: promote idiomatic functional Rust #372
style: promote idiomatic functional Rust #372
Conversation
Congrats, you made the code less readable and extensible. |
@leb-kuchen I'd be glad if you could elaborate on that. |
src/menu.rs
Outdated
let selected_iter = items.into_iter().filter(|i| i.selected); | ||
let selected_count = selected_iter.clone().count(); | ||
let selected_dirs_count = selected_iter.filter(|i| i.metadata.is_dir()).count(); | ||
(selections.0 + selected_count, selections.1 + selected_dirs_count) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This replaces one for loop with two
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'd been proven many times Rust's functional style gives compiler much wider field for optimisations than traditional imperative loops, especially if mutability is removed.
I can't run this through godbolt as they don't have criterion
available, but my benchmark for 1000000 items, shows 2.7281 ms for for
loop and 471.58 µs for filter()/count()
iterators on my notebook PC. For 10000 difference is smaller, but still filter()/count()
wins: 6.3825 µs / 4.7141 µs.
Style wise, I'd personally replace tuple
with own type supporting addition to right fields and logic queries, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This now O(n^2) instead of O(n).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait, are you folding over an option
Go write Haskell |
I've had similar questions when I first started contributing to COSMIC. Don't take these answers as official. They're simply how I understand the situation.
iced_aw and libcosmic depend on different versions of
COSMIC's development has been extremely fast. Some of the code quality issues you've mentioned is due to that alacritous dev time where finished features or bug fixes took precedence over tightly bound code. You'll notice throughout most of the codebases that there are TODOs to move certain implementations over to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Method | Avg t @ 10000 elements |
---|---|
original for |
6.4166 µs |
filter() /count() (supposed O(n²) |
4.5204 µs |
filter() /fold() (last one) |
3.6868 µs |
children.push(menu_item(fl!("rename"), Action::Rename).into()); | ||
children.push(menu_item(fl!("cut"), Action::Cut).into()); | ||
children.push(menu_item(fl!("copy"), Action::Copy).into()); | ||
children.extend(vec![ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The vec!
macro is not needed here, plain arrays implement IntoIterator
as well.
Also, wouldn't the use of extend
versus a series of pushes make it less convenient to conditionally add menu items, like (currently pending) PRs like #359 and #370 do?
I guess I don't see the benefit of using extend
. If it is to reduce the number of re-allocations as the children
vec grows, this should already be solved by initializing with with_capacity
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Point taken. This to me screams for kind of builder pattern, with something like .add_item()
and .add_item_if()
flat extensible list. Example:
let menu = MenuBuilder::new()
.add_item(fl!("open"), Action::Open)
.add_item_if(fl!("open-with"), Action::OpenWith, selected.exactly_one())
.build()
Don't hesitate to tell me if I'm hallucinating 😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally, for what the code does now this would be too much abstraction. After all, right now all the builder would do is add elements to a vector. I don't know what the "minimum number of builder methods to warrant a builder pattern" would be, but I think it's more than 2.
Also if I think about other places where builders are used, it's usually places where the creation is a complex process that needs to preserve certain invariants for every possible construction, and where different users create differently built objects. But here, there would only be one call to build()
, because this is a very internal use, compared to builders that are part of the public API of HTTP libraries, database query builders, command line parsers, etc...
If this was in libcosmic, this should definitely be a builder pattern.
But ultimately, this is a decision someone from the pop-os team should make.
I got the feedback for my proposal that I considered and I have some ideas how to tackle certain things in this code to make it more long term maintainable, but I have to start from scratch. |
I know this is unsolicited request, but as you're proud in writing Cosmic Desktop in Rust would be nice to promote more idiomatic functional style with less mutability. I don't mind if you throw this away as unsuitable for your needs.
Additionally I expanded the menu row to increase space between menu item name and the shortcut as translated items were too tight visually.
Working on this, however, led me to form following questions:
iced_aw
. Is there reason to build UI this way? Is there a roadmap whatlibcosmic
would include in terms of standard desktop widgets?HashMap<>
to be iterated over to find key binding for given action. Would it make sense to crate a type likeUIActionMap
that would hold some faster hash maps for both directions? For several key bindings in the application actually a vector with iterative search is faster than HashMap as actual hashing and bucketing takes plenty of cycles.