Skip to content

Add experimental array.find-index predicate - #12790

Open
tilladam wants to merge 2 commits into
slint-ui:masterfrom
tilladam:array-find-index
Open

Add experimental array.find-index predicate#12790
tilladam wants to merge 2 commits into
slint-ui:masterfrom
tilladam:array-find-index

Conversation

@tilladam

@tilladam tilladam commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Sibling to the array.any/array.all predicates (#11989): array.find-index((name) => condition) returns the index of the first element for which the closure holds, or -1 if none match.

Mirrors any/all end-to-end — parser/lookup, resolving, Rust and C++ codegen, api/cpp/include/private/slint_models.h, the interpreter, and core::model::model_find_index — reusing the existing Expression::Closure/Type::Closure plumbing. Same experimental gate as any/all (tracked by #12777); only usable inline, not stored or passed around, for the same reasons any/all are restricted that way.

The interpreter's per-row "evaluate the closure against this row" loop is shared with any/all via a small eval_array_row_predicate helper rather than copy-pasted into a third arm — any/all/find-index differ only in what they do with each row's boolean result.

This is part 1 of a 3-PR stack:

  1. This PRarray.find-index, the experimental predicate.
  2. Add stable array.index-of, desugaring to find-index #12791array.index-of, a stable value-based sibling that desugars to find-index.
  3. ComboBox: reverse-lookup current-value writes into current-index #12792 — wires ComboBox's current-value writes to index-of, closing ComboBox and RadioGroup: writes to current-value are silently overridden #11970.

Test plan

  • cargo test -p i-slint-compiler --features display-diagnostics --test syntax_tests
  • SLINT_TEST_FILTER=array_predicates cargo test --manifest-path tests/Cargo.toml -p test-driver-{interpreter,rust,cpp,nodejs}

Comment thread internal/core/model.rs Outdated
(0..model.row_count())
.find(|index| {
model.model_tracker().track_row_data_changes(*index);
predicate(model.row_data(*index).unwrap_or_default())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

How about something like

Suggested change
predicate(model.row_data(*index).unwrap_or_default())
model.row_data(*index).is_some_and(predicate)

Then we don't need T:Default

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done — applied the suggestion and dropped the T: Default bound.

Comment thread internal/core/model.rs
model.model_tracker().track_row_count_changes();
(0..model.row_count())
.find(|index| {
model.model_tracker().track_row_data_changes(*index);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this might be expensive to track all row like that.
we might need a better system to track_any_change or something like that.

anyway, the any and all have the same problem.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed — since any/all have the same per-row tracking cost, I'd rather tackle a track_any_change-style mechanism for all three in a follow-up than special-case find-index here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Follow-up implementing track_any_change: #12806

Comment thread internal/interpreter/eval.rs Outdated
/// Evaluates `predicate` against each row of `model`, binding `arg_name` to the row value.
/// Stops as soon as `on_result` returns `Some`; shared by `ArrayAny`/`ArrayAll`/`ArrayFindIndex`,
/// which differ only in what they do with each row's boolean result.
fn eval_array_row_predicate<R>(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should be able to re-use i_slint_core::model::model_find_index

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done — the interpreter now reuses model_any/model_all/model_find_index from core for the iteration and dependency tracking; what's left in eval.rs is only the per-row "bind the closure arg, evaluate, restore the shadowed local" part that's inherently interpreter-specific.

Sibling to the array.any/array.all predicates (slint-ui#11989): returns the index
of the first element for which the closure holds, or -1 if none match.
Mirrors any/all end-to-end — parser/lookup, resolving, Rust and C++
codegen, api/cpp/include/private/slint_models.h, the interpreter, and
core::model::model_find_index — reusing the existing Expression::Closure/
Type::Closure plumbing. Same experimental gate as any/all (tracked by
slint-ui#12777); only usable inline, not stored or passed around, for the same
reasons any/all are restricted that way.

The interpreter reuses core's model_any/model_all/model_find_index for
the row iteration and dependency tracking, rather than open-coding the
loop a second time; a small eval_array_row_predicate helper does the
per-row work shared by any/all/find-index (bind arg_name, evaluate the
closure expression, restore the shadowed local var).

Added as groundwork for a real fix to slint-ui#11970 (ComboBox/RadioGroup
current-value writes), which needs a value-to-index reverse lookup that
plain any/all can't express.

changelog: Added an experimental `array.find-index((name) => condition)` predicate, returning the index of the first matching element or -1.
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