Skip to content

Lower more of Elixir's stdlib on Rust's collections - #26

Merged
dannote merged 13 commits into
masterfrom
stdlib-gaps
Oct 26, 2026
Merged

dannote merged 13 commits into
masterfrom
stdlib-gaps

Conversation

@dannote

@dannote dannote commented Oct 26, 2026

Copy link
Copy Markdown
Collaborator

Gaps found while rewriting Figler's defrust code in plain Elixir style: comprehensions, Enum.reduce, Map/MapSet, list patterns, []/%{}, and interpolation instead of .iter(), HashMap::new(), deref, and ref.

  • Collection basics lower onto Rust's collections. This covers:
    • Map.update/4, which matches the present value;
    • List.replace_at/3;
    • Enum.sort/1 on ordered lists;
    • MapSet.new/1 of listed items (HashSet::from);
    • String.downcase/1 and String.upcase/1;
    • x in [literals], which becomes an array check;
    • comparisons with [], %{}, "", and nil, which become is_empty() and is_some()/is_none().
  • Updates happen in place. ++ and Map.update grow an owned list or map instead of cloning it. A for … reduce: with one variable clause names its accumulator after that variable and annotates its type.
  • Borrowed values are read, not moved. This applies to:
    • copy items a slice lends;
    • values bound by a pattern on a reference, in comparisons, and/or, and if;
    • an option field reached through a reference, which is matched by reference;
    • a list held by an earlier generator's item, which a nested generator iterates;
    • a list of tuples still in use, which a comprehension reads by reference instead of cloning each item.
  • Typing is more complete.
    • Branching expressions, comprehensions through every generator, block bindings in order, reductions, indexing, and nested aliases are typed.
    • A binding keeps the option type its right-hand side gives; this previously produced a double .as_ref().
    • A tuple destructured from a fallible call has typed elements.
    • Counts take the integer type expected of them.
  • Desugaring covers more forms.
    • String interpolation becomes format!.
    • A multi-generator for … reduce: becomes Enum.reduce over a comprehension.
    • An irrefutable function argument is bound with =.
  • Rendering fixes.
    • A list literal passed for a slice is &[..], not &vec![..].
    • An untyped %{} is HashMap::new().
    • A string-literal key is passed as the &str it already is.
    • A range bound that can fail propagates its error.
    • A range used as a method receiver is parenthesized. It used to render as 1..=n.into_iter(), which doesn't compile.

mix ci passes (920 tests). With this branch, Figler regenerates clippy-clean (-D warnings), and its tests pass apart from the package test, which can't build a Hex package while dependencies are local paths.

length, map_size, and MapSet.size are len(), a usize where one is
expected, as is arithmetic on them there. Enum.empty?/1 and comparing with
[] or %{} are is_empty(). %{} and %{key => value} build a map where one is
expected. A generator over a map or set behind a reference iterates it by
reference, copying copy keys and values. first..last//step counts up
between dynamic bounds, and ++ concatenates lists.
…catenation

A comprehension's type binds each generator in turn, Some and Ok patterns
included, so a map it builds is typed; a tuple bound with = types each
variable; a Vec read from Rust is a list. A map is indexed by a borrowed
key. Collecting into a slice argument borrows a collected Vec. concat
and zip take a list only at its last use. An iterator collected into a
list and iterated again is iterated directly, and a generator over one
value computes it before the item moves.
…e in use

Enum.reduce builds its accumulator as the expected type and is typed by
its reducer when its initial value is not. Indexing a map or list is
typed, nested aliases resolve, and types rebuilt from Rust syntax keep
their kinds. Values put into a list or pushed while still in use are
copied, a value behind a reference is copied rather than updated in
place, and list patterns bind items by reference.
… closures

Enum.sort sorts booleans, strings, and tuples. Map and set generators are
typed, specs of maps and sets keep their key and value types, and an
Elixir integer binding takes the integer type its uses need. %{} builds a
map where a reference to one is expected, unknown keys are borrowed, and
a closure only calling a function is that function.
…, propagate fallible concatenation, clone through double references
… literal membership, and iterate in-use tuple lists by reference
@dannote
dannote merged commit f3bee7c into master Oct 26, 2026
2 checks passed
@dannote dannote mentioned this pull request Oct 26, 2026
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.

1 participant