1.0.0-rc.19 - 2026-10-26

Added

  • What std's methods return is known wherever the receiver's own type parameters decide it: name.as_ref() on an &Option<String> is an Option<&String>, and list.first() an Option<&T> of its items. Type.substitute/2 binds a type's parameters.
  • A list literal whose items share a type is a list of that type.

Fixed

  • Enum.at on a pipeline stage is the iterator's nth, collecting no list.
  • A filter over copied tuples matches each by reference, as |&(set, field)|.
  • Enum.count, length, and byte_size stay usize where one is expected.
  • A function a defrustmacro defines can name the module's own types in its spec, as a defrust can.
  • A fallible call used as the receiver of a method its result lacks, as decoder.read_bool().encode(env), propagates its error first.
  • A loop over a slice, or a list behind a reference, reads each item by reference, so passing one on where a reference is expected borrows it once.

1.0.0-rc.18 - 2026-10-26

Added

  • A pipeline of Enum stages runs as one Rust iterator chain: list |> Enum.map(f) |> Enum.find(p) is .map(f).find(p), with no list collected between them, and Enum.count/1 on a stage counts as it goes.
  • mix rustq.std also derives what std's methods on strings, characters, lists, maps, sets, and options return where the receiver's type parameters do not matter, so name.len() is a usize and name.trim() a &str.
  • A comprehension's type is the list, map, or set it collects its body's values into, and a block of expressions lowers wherever an expression does.

Changed

  • Whether a value may be taken is decided for each use of it: a pass over the body records what is still in use after every variable occurrence, as Elixir scopes it, so a list used again later in the same expression, as {Map.put(scores, name, 1), scores}, is copied. A parameter is mut where the lowered Rust changes it in place.
  • Map.new, MapSet.new, Enum.into into an empty collection, group_by, uniq, and generators over Enum.with_index are rewritten into other Rusty-Elixir before lowering, by RustQ.Meta.Desugar.
  • Pattern, comprehension, path, and mutability lowering are their own modules beside RustQ.Meta.Lower.
  • Each Elixir module's lowering implements RustQ.Meta.Lower.Stdlib.Lowering, whose use imports the helpers they share, and a call goes to the lowering named after its module. Lowering builds Rust with RustQ.Rust.AST.Builder, whose expr/1 now accepts every RustQ AST node.

1.0.0-rc.17 - 2026-10-25

Added

  • A module's defrustmod mappings come with it under the alias naming it, and defrustmod __MODULE__, as: [...] maps the module itself, so a library maps the Rust modules its generated code defines once for every module using it.
  • MapSet lowers on HashSets, MapSet.t(item) is a HashSet, and Map lowers on HashMaps as well as structs: get, fetch!, has_key?, put, delete, merge, update/4, keys, values, to_list, and new, and MapSet.new, put, delete, member?, union, difference, intersection, subset?, disjoint?, size, and to_list. Updates take the collection only at its last use.
  • Enum.into, group_by, with_index, find_index, find_value, reduce_while, sort_by, max_by, min_by, max, min (with an empty fallback), uniq, uniq_by, dedup, split_with, at, slice, take_while, drop_while, zip_with, chunk_every, join, and map_join. A comprehension over Enum.with_index(list) enumerates the list in place, and into: HashMap.new() or MapSet.new() collects into that collection where no type is expected.
  • Rebinding a list, map, or set to its update, as scores = Map.put(scores, name, 1) or layers = Enum.sort_by(layers, ...), changes it in place: scores.insert(name, 1);.
  • A comprehension generator over one value, as {:some, id} <- [record_id(record)], matches the value beside the item instead of iterating a list made for it.

Changed

  • Enum functions take an owned list only at its last use. A list used afterwards, by the call's closure, or by a value built from it is borrowed, so code using the list again compiles. Questions such as any? and all? always borrow, items kept from a borrowed list are cloned, and numbers and atoms are copied out.
  • A new value such as HashSet.new() passed where a reference is expected is borrowed.
  • A list reached through a reference, as binding.superseded for a borrowed binding, is never taken: Enum borrows it and a comprehension copies its items, as a comprehension also does for an owned list still in use.
  • Enum.filter/2, reject/2, find, and count/2 lower to Rust's filter and find, as clippy prefers to filter_map returning the item; a number or atom item is matched as |&value|. Enum iterates slices with iter(). RustQ.Rust.AST.PatRef renders &pattern.

1.0.0-rc.16 - 2026-10-25

Added

  • A RustQ module provides the .rs file named as its own file is in templates/ beside it, such as templates/runtime.rs for runtime.ex: Rust it emits as it is, like a runtime a library ships. Its functions, methods, and types join the module's exports, each type as an Elixir type, and __rustq_items__/0 and __rustq_source__/0 emit it. use RustQ.Meta, provides: [...] names other files, relative to the module's file. A library no longer needs to publish where its Rust files are.
  • A RustQ module named through alias in a spec, a type, or a defrust body is imported as callable_modules imports one, and calling its Rust function as Module.function(...) lowers to the function's call. Other aliases remain Rust paths.

1.0.0-rc.15 - 2026-10-24

Added

  • match?(pattern, value) lowers to a match answering whether the value matches, so defrust bodies need not spell it as a case returning true and false.

1.0.0-rc.14 - 2026-10-23

Added

  • RustQ.Meta.AST.macro_call!/3 takes RustQ AST for :expr and :ty captures and renders it, so a generator need not write Rust source strings for them.

Changed

  • use RustQ.Meta, rust_sources: ... evaluates its paths when the module compiles, so they can come from a module attribute or a function, such as one returning the path of a Rust source a library ships. Relative paths are still relative to the working directory, which a library loaded from another project cannot rely on.

  • defrustmacro repetition rows separate their captures with commas, in the pattern and in the calls RustQ.Meta.AST.macro_call!/3 builds, for every row. RustQ used to lay out a few row shapes, written for kiwi_codec, with => and :, and fall back to spaces otherwise, which expr captures cannot be followed by.

  • RustQ.Rustler.Term's struct helpers no longer name rustler's raw term type, which rustler 0.39 changed from rustler::wrapper::NIF_TERM to rustler::sys::ErlNifTerm. cached_struct_keys caches Atoms, default_struct_values returns Vec<Term>, and make_struct_from_nif_term_arrays is make_struct_from_arrays, taking atom keys and term values. The same Rust builds on rustler 0.37 to 0.39.

  • RustQ.Native crates depend on rustler 0.39 by default, and mix rustq.templates.check and the suite's generated-Rust check build against it.

Removed

  • RustQ.Rustler.Nif.raw_term_builders/1. Its helpers called rustler::wrapper, which rustler 0.39 removed; RustQ.Rustler.Term.builders/1 builds the same maps and structs from terms.

Fixed

  • assign!(index(values, i), call()) propagates a fallible call with ? when the element type is known, as a let with a known type does, and makes an owned values argument mut. The Rust assigned the Result and changed an immutable argument.
  • A defrust inside an item defrustmacro keeps its plain arguments plain. Every argument used to be written as a macro variable, $offset, which the body then referred to as offset.
  • Calling a function a defrustmacro names with an :ident capture calls $inner(...) rather than a function literally named inner.

1.0.0-rc.13 - 2026-10-23

Added

  • A type declared twice in a module is a :duplicate_type diagnostic naming both lines. RustQ reads types before Elixir rejects the duplicate, and used to follow the later declaration silently; types read from source with RustQ.Spec were never checked at all.
  • RustQ.Spec.declarations/1 takes declarations' lines from quote location: :keep as well as from parsed source.

Changed

  • A typespec RustQ has no Rust type for, such as pid(), keyword(), map(), a bare nil, or a literal 1 or 1..10, is an :unsupported_type diagnostic suggesting a RustQ.Type spelling. It used to become a Rust type named after it, which never compiled. any() is Term, as term() is.
  • byte() is u8, non_neg_integer() and pos_integer() are u64, and char() is u32, since it includes surrogate code points Rust's char cannot hold. pos_integer() admits zero, and rustler's u8 decoder truncates out-of-range integers rather than rejecting them.
  • Which words are Rust keywords, written as raw identifiers, comes from syn rather than a list kept in RustQ, in the native and the text renderer alike.

Fixed

  • A function returning R.nif_result(t) returns a plain value as Ok(value), as the NIF returns that value in Elixir. Its Rust returned the bare value, which did not compile; a plain value returned where an R.result is expected is now a diagnostic asking for {:ok, value}.
  • A tail decode_as! in such a function is its decode, without the ? that made it not compile.
  • :ok returned where an atom is expected is the atom, and Ok(()) for any result carrying nothing; it used to be () wherever the type was not NifResult<()>.
  • {:some, value} and :none where an option is expected are Some(value) and None, as patterns read them; {:some, 12.0} used to be a tuple.
  • A function returning a variable typed as a 3-tuple returned the variable's Elixir AST as a tuple of atoms.
  • true and false render as raw identifiers wherever a name goes, and macro_rules! names and grouped use names are escaped as other names are.
  • Rendering a node on its own escapes keywords in variables, fields, methods, closure arguments, patterns, and atom names, as rendering it inside an item does.
  • A name Rust cannot use as an identifier, such as a field named self, is a diagnostic carrying syn's message. It used to render invalid Rust, fail with "argument error", or crash the native renderer.
  • Rendered Rust is checked to parse before it is returned.
  • RustQ.Rust.AST.Builder.expr(true) builds the literal, not a variable named true.
  • A RustQ.Native crate using %{key => value} or HashMap.t in a NIF compiles; it imports HashMap.
  • A public R.enum type crosses the NIF boundary as Elixir writes it, as {:circle, 1.0} or :empty, through rustler::NifTaggedEnum. It had no codec, so a NIF taking or returning one did not compile.
  • A defnif whose spec returns binary() returns an Elixir binary. Its bytes are a Vec<u8> in Rust, which rustler sends as a list, so the NIF now wraps a *_rustq_binary_impl function and copies its bytes into a new binary. A binary() nested in an argument or result still crosses as a list of bytes.
  • gen, which the 2024 edition reserves, renders as a raw identifier.
  • A path segment named try, or a variable named break, continue, return, or yield, renders raw. It used to render as the keyword, which parses but does not name the variable.

1.0.0-rc.12 - 2026-10-22

Added

  • Lower comprehensions with several generators, filters, and patterns that can fail to match. As a value, a comprehension becomes an iterator pipeline that collects into the expected type, so into: works by the target type; run for its effects, it becomes nested for loops with if let and if.
  • Lower &fun/1 captures in any expression, not only inside Enum calls, and &(&1 + 1) shorthand to a closure, including as an Enum mapper.
  • Lower container[key] to Rust indexing, borrowing the key for a map, also through a reference to one.
  • A spec's %{key => value} with one key type is a HashMap, as HashMap.t(key, value) is.
  • Match constants such as Kind.PLAIN in patterns, resolved through the module's Rust module aliases, and true or false in with clauses.
  • Add R.ref/2 and R.mut_ref/2 for references with a named lifetime, as in R.ref(Record.t(), R.lifetime(:a)), which a function returning a reference borrowed from one of several reference arguments needs.
  • Add R.hashable(type) for a declared struct or enum that also derives PartialEq, Eq, and Hash, so it can key a map or set.
  • Build and match R.enum values with tagged tuples, as in Elixir: {:float, value} is Literal::Float(value) and :missing is Literal::Missing wherever the enum is expected or matched, including through a reference and inside a type that refers to itself.
  • Generate Rust-only types from @typep: the struct, enum, or alias without the term decoder a @type gets, so it may hold values without a term codec.
  • Lower %{value | field: new} on a struct to Rust's struct update syntax, Struct { field: new, ..value }, including on the items a comprehension or Enum function's closure is given; a struct a closure captures is cloned.

  • Closures that a comprehension or Enum.map, flat_map, each, reduce, any?, or all? gives a typed list's items know their type.
  • A later generator over a list from outside the comprehension copies its items, so for x <- xs, y <- ys compiles without cloning ys by hand.
  • Add R.slice(type, R.lifetime(:a)) for a slice with a named lifetime.
  • An atom where an atom-union type such as :small | :large is expected names the enum variant, as patterns already did, including fields of a struct_literal whose struct the module declares.

  • A module may use the types its callable_modules declare, written remotely as Other.tree() since Elixir checks local types; they keep their single Rust declaration in the module that declares them.
  • RustQ.Syn describes traits, as RustQ.Syn.Trait with their associated types and methods, and RustQ.Syn.Index.traits/1 lists them. Impls carry their type parameters' trait bounds and the associated types they define; methods carry their attributes and type parameters' bounds. Impls also carry their trait's structured path as trait_ast, and negative marks impl !Trait. RustQ.Syn.parse(source, unstable: true) reads source that uses unstable syntax which does not change an item's signature, as the Rust standard library does.

Changed

  • Which std methods mutate their receiver, return an iterator or an option, belong to Option and Result, or belong to std collections is derived from the Rust standard library's source rather than listed by hand, so lowering knows every stable method, such as push_back, retain_mut, or nth.
  • A callable's arguments are RustQ.Binding.Callable.Arg structs rather than bare maps with the same keys.
  • Piping into a function the module can call passes the value as its first argument, as in Elixir. It used to call a method of that name on the value.
  • An Elixir predicate such as live? names the Rust function live; a module defining both is rejected.
  • Document every module and function that was hidden from the API reference. Lowering, code generation, native build, and tooling internals are grouped as such, and the user-facing groups list only user-facing modules.
  • Stub modules RustQ.Rustler.Nif generates carry a moduledoc, so checked-in generated stubs need regenerating.
  • The API reference leaves out the tools in dev/ for working on RustQ itself, which the package does not ship.

Fixed

  • Elements of a tuple pattern are full patterns, so {{:some, Kind.PLAIN}, _} matches the constant inside the option.
  • A pipe into a name the module does not define lowers as that method call, with the same argument types and borrows; a let bound to a collect() states what it collects into, a Vec unless known.
  • A binding used through option methods such as is_some keeps its option type, a vector's items are not taken for its success type, and methods every collection has, such as is_empty, no longer type a binding as whichever source type also defines them. clone of a borrowed struct and to_vec are typed.
  • ok(value) lowers the value as the expected success type, so ok(:ok) is Ok(()); tuple literals are typed when their elements are, so atom and constant patterns match inside them.
  • Mark mut a tuple-destructured binding that changes, and an owned argument changed through a field or borrowed with mut_ref. A field named like a changing method, such as layer.swap, does not mark its struct mut.
  • Pushing a call that fails as the function does onto a vector of unknown items propagates it; collecting where a slice is expected collects into a vector and borrows it; an option or result binding nothing downstream unwraps keeps its type.
  • Write the Rust Clippy expects: a comprehension over values.iter() does not call into_iter() again, one that clones each item is cloned(), into_iter() on a borrowed collection is iter(), and a statement case whose other branch does nothing is an if let.
  • A closure Option::map or and_then takes lowers its body as the expected option's value, so tagged tuples and atoms inside it name variants; an option a closure maps, items collected, or an iterator's next is not wrapped in a second Some. or, or_else, as_slice are typed.
  • Patterns in with, inside {:some, _}, and on slices take the type of what they match, and a list pattern on a value of unknown type is a slice pattern.
  • Inference reads a struct literal's fields as the types the struct declares, ignores uses after a pattern binds the same name again, and a comprehension ignores item variables its body does not use.
  • A failed native render names atoms matched inside patterns no type resolved.
  • A borrowed number, boolean, or tuple of them passed where the value is expected is copied out with *, and arithmetic on borrowed numbers is typed as a number; ref(value) is typed as a reference, and try_fold in an option-returning position is not wrapped in Some.
  • A comprehension whose collection type is known lowers its body as the item type, so tagged tuples and atoms in it name variants, and extend on a vector takes items of its type.
  • A with run for its effects whose else does nothing lowers to nested if lets, with true <- condition as an if; variables a slice pattern binds are typed as references to its elements.
  • Keep the grouping of nested operators: a and (b or c) rendered as a && b || c, and not (a or b) as !a || b. Operands are parenthesized by Rust's precedence.
  • A cond run for its effects lowers to if statements, and a comprehension bound in a statement states what it collects into, a Vec unless known.
  • An explicitly borrowed argument is not borrowed again, and a method that changes its receiver marks it mut wherever it is called.
  • Render words Rust reserves for future use, such as override and yield, as raw identifiers, including function and closure arguments and local calls.
  • Compare two options or results without propagating either side, never propagate literals, and type arithmetic on a propagated option as its value, so some(value + 1) no longer wraps the sum in a second ?.
  • Parenthesize a ? applied to a compound expression; it used to bind only to the last operand.
  • Mark a variable mut when a statement-level method chain starting with a method that changes it, such as map.entry(key).or_default().push(value), uses it, and a closure argument its body changes, such as a fold accumulator. An empty map or set bound with a known type states it, as Rust cannot infer its value type through entry(key).or_default().
  • Lower a with whose else is a lone catch-all to that fallback arm, rather than a single-arm match on the unmatched value, and a borrowed list literal to a borrowed array rather than a vector built for the borrow.
  • Patterns of unknown type drop the old types of the names they rebind, and a with clause takes the type of a plain variable it matches, so a rebound value is no longer propagated as the option it came from.
  • A with without else passes an unmatched {:error, _} or nil on as the function's own Err or None, as Elixir returns the unmatched value.
  • Never propagate with ? in a function or closure returning a plain value such as a vector; the value stays wrapped there.
  • Type get on maps and on borrowed vectors, and_then, copied, cloned, and methods reached through a pipe, so an option they return is no longer wrapped in a second Some.
  • A function returning nothing runs its last expression as a statement, so a trailing if or for no longer becomes a value such as a collected vector.
  • ref([]) where a slice is expected borrows an empty array.
  • A type that refers to itself, such as a tree whose children are trees, names its Rust type in its own fields.
  • Mark an owned argument mut for any method that changes it in place, such as retain or sort, not only push and insert.

1.0.0-rc.11 - 2026-09-30

Added

  • Describe enum variant payloads in RustQ.Syn.Enum.variant_shapes, a list of RustQ.Syn.Variant structs with the variant name, its kind (:unit, :tuple, or :named), its fields, and its doc comments. variants still lists variant names.
  • Report lifetimes and type_parameters for RustQ.Syn.Struct and RustQ.Syn.Enum, matching the existing function and method metadata.
  • Add RustQ.Rustler.Term.encoders_from_source/3 and encoder_atoms_from_source/3, which build Term encoder functions and their atom declarations for structs and enums read from Rust source. They support types owned by another crate and without serde. Any reachable type that is not indexed, a wrapper, a scalar, or mapped as external fails generation with a list of the unmapped types, as does a variant tag that would replace a payload field with the same key. Per-type policy covers excluded fields, key and variant renames, with: helpers, and transparent: true structs.
  • Add RustQ.Syn.Index.structs/1, struct/2, and struct!/2.
  • Add RustQ.Rust.AST.PatternBuilder.tuple/1.

Fixed

  • Render Rust keywords used as field names, struct literal and struct pattern fields, and macro item arguments as raw identifiers (r#type). Field access such as value.type previously failed to render.
  • Type a literal atom such as :if_node in a typespec as Atom, like a union of atoms. It was emitted as a Rust type named after the atom, which does not compile.

1.0.0-rc.10 - 2026-09-28

Fixed

  • Resolve local type alias references regardless of declaration order. An alias referenced inside a list, tuple, or map value from an alias that sorts before it, such as [plugin()] in lint_input, was emitted as the raw Elixir name (Vec<plugin>) instead of its Rust type (Vec<Plugin>).
  • Stop passing fast_path: to JSONCodec, which json_codec 0.3 deprecates, so RustQ compiles without warnings on json_codec 0.1 through 0.3.

1.0.0-rc.9 - 2026-09-06

Added

  • Support named fields and attributes on Rust enum variants, and attributes on match arms, enabling feature-gated event declarations and dispatch.
  • Add explicit move captures with move(fn ... end), tuple-pattern closure parameters, and structural closure arguments with optional Rust types.
  • Add R.impl for native callable types using ordinary Elixir function signatures, with :fn, :fn_mut, and :fn_once kinds, additional traits, and explicit lifetime bounds.
  • Preserve foreign callable-trait parameter and return types, and specialize supported generic method arguments from known receiver and argument types.
  • Expand supported source-defined Rust type aliases for receiver inference, including generic aliases and grouped imports. Resolve known standard-library shared pointers, mutex lock results, and guard receivers without guessing from unrelated method names.

Fixed

  • Render matching struct-field bindings in shorthand form for Clippy-clean generated patterns while preserving explicit renamed bindings.
  • Reject enum variants that mix tuple and named fields.
  • Preserve reference and unary-expression precedence in method receivers.
  • Infer borrows for dereferenced values and supported String and Vec clone results, including expressions derived from typed callback parameters.
  • Keep closure parameter types scoped to the callback instead of inheriting shadowed outer-variable types.
  • Omit redundant unit returns, use struct-expression field shorthand, and simplify supported ignored-error result matches for Clippy-clean output.
  • Avoid unnecessary mutable bindings for local references while preserving explicit mutation requirements.
  • Respect generic-parameter shadowing during alias expansion, refresh aliases after source edits, and diagnose conflicting generic substitutions.
  • Reject unsupported lifetime-alias expansion and inconsistent specialization of opaque foreign trait bounds rather than silently changing their meaning. Defer unsupported nested or ambiguous alias diagnostics until expansion requests them.

1.0.0-rc.8 - 2026-09-06

Added

  • Add @cfg for conditional Rust helper, method, and NIF implementations with shared signatures and separate clause groups.
  • Support nested attribute metadata, including not, all, and any predicates, with typed path, name/value, and list representations.
  • Select conditional functions with RustQ.Meta.AST.function!/3 and a :cfg option.

Changed

  • Reject ambiguous function selectors instead of choosing the first conditional implementation.
  • Diagnose stacked or dangling conditions, duplicate conditions, mixed unconditional/conditional definitions, and inconsistent NIF scheduling policies.

Fixed

  • Preserve authored receiver names and lexical shadowing in defrustimpl methods through a local binding to the Rust receiver.
  • Gate generated NIF result codecs and implementation helpers consistently with their conditional entrypoints while retaining one Elixir export.

1.0.0-rc.7 - 2026-09-06

Changed

  • Reject integer operands for / with an actionable diagnostic; use explicit floating-point conversion when floating-point division is intended.
  • Validate generated Cargo dependency names, options, and duplicates, and serialize manifests with a TOML encoder for correct string escaping.

Fixed

  • Load generated native libraries correctly on macOS.
  • Preserve native result types through NIF boundary encoding, recursive calls, composed calls, closures, and named captures; diagnose generated-name conflicts.
  • Preserve expected result types in cond branches and typed Enum.map closures.
  • Resolve local type aliases in decoding expressions and external struct constructor paths consistently with their remote typespecs.
  • Infer the decoded value type for return-position .decode() and optional return types for checked integer arithmetic methods.
  • Preserve argument evaluation and infer result propagation for shape queries.
  • Refresh callable metadata after provider recompilation and source changes, including same-size edits with unchanged timestamps.
  • Render nested conditionals as else if where appropriate for Clippy-clean output.

Security

  • Update maintainer HTTP dependencies to versions without the reported security advisories and remove unused dependency lock entries.

v1.0.0-rc.6 - 2026-07-27

  • Infer mutable owned Rust arguments from mutating collection method calls and render them directly as mut parameters, including metadata-backed mutable receivers and supported Vec and set/map insertion methods.

v1.0.0-rc.5 - 2026-07-27

  • Add focused RustQ.Meta.AST selectors for generated Rust items, type items, enums, structs, and inherent or trait implementation blocks.

v1.0.0-rc.4 - 2026-07-27

  • Add defrustimpl for generating inherent and trait Rust implementation methods directly from typed Rusty-Elixir, including &self and &mut self receiver inference and implementation visibility, attributes, and lifetimes.

v1.0.0-rc.3 - 2026-07-17

  • Generate optional Rustler term filters with typed Option::filter calls for compatibility with Rust 1.97 Clippy.
  • Highlight fenced Rust examples in generated HexDocs.

v1.0.0-rc.2 - 2026-07-16

  • Add composable RustQ.Test.rust_source!/1,2 and nif_exported?/3 helpers that preserve ordinary ExUnit assertions and diffs; reorganize the suite around focused source values, parameterized schema/corpus cases, compiled support fixtures, and ExUnit-managed temporary directories.
  • Decode precise-capturing impl Trait + use<'a> bounds without relying on pre-existing runtime atoms, as exercised by current skia-safe metadata.
  • Funnel compiled defrust AST access and internal rendering through the documented RustQ.Meta.AST and RustQ.Rust boundaries.
  • Preserve inferred tuple types as structural RustQ.Rust.AST.TypeTuple nodes instead of raw rendered type strings.
  • Preserve imported Rust reference lifetimes and fixed-array lengths, lowering them through structural TypeRef, TypeArray, and TypePath nodes.
  • Add structural bare-function types with bound lifetimes, unsafe and ABI qualifiers, named input metadata, variadics, and return types.
  • Preserve ordered Rust path generic arguments and impl Trait bounds in Syn metadata, and lower complete bound lists through structural TypeImplTrait.
  • Document defnif normal, dirty CPU, and dirty IO scheduler policy and the preferred @nif schedule: :dirty_cpu | :dirty_io spelling.
  • Replace the README reference manual with a focused project overview and move template, AST, splice, metadata, and validation details into a generation guide.
  • Lower supported remote calls in Elixir pipelines and carry expected collection types through materialized Enum stages, preserving eager semantics while avoiding ambiguous Rust collect() inference.

v1.0.0-rc.1 - 2026-07-16

  • Define the RustQ 1.x compatibility contract and ship the policy with the package, HexDocs, and agent skill.
  • Add RustQ.Native, public defnif, private defrustp, generated Cargo crates, native loading, Cargo dependency metadata, and dirty-scheduler normalization for zero-handwritten-bridge-Rust NIFs.
  • Derive directional Rustler boundaries for scalars, strings, binaries, tuples, lists, options, tagged results, typed maps, Elixir structs and exceptions, structural unions, unit enums, and explicitly declared resources.
  • Expand Rusty-Elixir lowering with multiple clauses, recursion, list/map/struct patterns, guards, comprehensions, and structural slice-pattern AST support; normalize semantics-preserving Kernel, Enum, List, typed Map, String, Tuple, and Range calls through focused hidden lowerers with call-specific typing.
  • Add external/precompiled-crate native preparation through use RustQ.Native, build: false, load: false, with public RustQ.Native.items/1 and implicit nif_env/0 injection that preserves BEAM arity.
  • Add packaged RustQ.Test ExUnit assertions for focused defrust source, defnif exports/attributes, and generated Rust validity.
  • Expose all compiled defrust function AST nodes through the documented RustQ.Meta.AST.functions/1 generator boundary.
  • Validate documented public APIs, package contents, checked generation, and a formatted, Clippy-clean zero-Rust native crate through structured external consumer fixtures containing no checked-in Rust or Cargo files.

v0.11.1 - 2026-07-14

  • Lower named Enum.map/2 function captures and multi-statement closure bodies, and dogfood them for generated native AST collection decoders.
  • Generate Rustler term decoders structurally, including cached struct keys, and add an AST-backed unsafe block for isolated low-level Rustler operations.

v0.11.0 - 2026-07-13

  • Require structural Rust type nodes at the native rendering boundary, normalize builder type inputs, and add structural tuple types.
  • Support multiple lifetimes on Rust struct AST nodes, hide the internal AST renderer, and remove duplicate simple-item builder delegates.
  • Validate atom-backed Rust identifiers and avoid callable metadata cache locks during parallel compilation.

v0.10.0 - 2026-07-10

  • Make RustQ.Rust.AST the canonical generated-Rust representation, including source-derived NIF wrappers with multiple lifetimes, and remove the duplicate legacy Rust builder model.
  • Reorganize Rustler generation into focused Nif, Term, Atom, Opts, Resource, and Schema APIs with concise wrapper, function-metadata, stub, and encoder naming.
  • Simplify generator manifests, template rendering, metadata selection, and file-spec APIs; hide implementation modules and document the public API by responsibility.

v0.9.11 - 2026-07-10

  • Discover Rustler atom references through a configurable Rust module name, including references nested in macro token trees.

v0.9.10 - 2026-07-10

  • Preserve explicit nested Option constructors and infer Option::map(Some) without adding extra wrappers or propagation.
  • Add a source-generated optional-string map decoder and emit zero-arity Elixir NIF stubs without parentheses.

v0.9.9 - 2026-07-10

  • Preserve nested wrapper tuple binding types and lexical pre-shadow types so downstream inference does not add propagation to infallible values.

v0.9.8 - 2026-07-10

  • Select map-backed Rust struct items from Rusty-Elixir @type metadata with configurable derives, attributes, struct visibility, and field visibility.

v0.9.7 - 2026-07-10

  • Add structural schema field and shorthand-field splices for projection-driven Rust struct declarations and literals.

v0.9.6 - 2026-07-10

  • Generate one Elixir NIF stub module from mixed Rust source metadata and RustQ AST functions, including automatic Rustler Env exclusion.

v0.9.5 - 2026-07-10

  • Preserve no-parentheses function-pointer field access in expected-type positions instead of lowering fields named decode as generic Rustler method calls.

v0.9.4 - 2026-07-10

  • Add AST-backed Rustler term encoders with nested, conditional, optional, collection, adapter, helper, and fallback field projections.
  • Add source-derived Rustler NIF wrappers and Elixir fallback stubs so one manifest and the handwritten Rust implementation signatures determine both sides of the boundary.
  • Discover atom references inside Rust macro token trees and document stable, structural Rustler generation workflows.
  • Add optional Rustler map decoders and improve option/method result inference.

v0.9.3 - 2026-07-06

  • Infer Option<T> author input for expected Option<&T> adapter arguments, including fallible NifResult<Option<T>> values passed to impl Into<Option<&T>> Rust methods.
  • Preserve owned option locals while emitting .as_ref() at the eventual call site to avoid borrowing from temporaries.

v0.9.2 - 2026-07-06

  • Improve Rusty-Elixir propagation and borrow inference for fallible option cases, some(...), unwrap_or, decode_as!, map_get, fallible reference access, list/slice arguments, loop bindings, and method arguments.
  • Improve callable metadata lookup for qualified method receivers and add corpus coverage for impl Into<Option<T>> argument propagation.
  • Refresh README, guide, skill, and module docs around inference-first authoring with less explicit unwrap!, ref, and mut_ref boilerplate.

v0.9.1 - 2026-07-06

  • Improve expected-input inference for callable arguments, including fallible case scrutinees, associated calls, and slice/Vec borrow adaptation.
  • Add corpus coverage for semantic defrustmacro item calls and recent propagation/borrow inference regressions.

v0.9.0 - 2026-07-06

This release is a large Rusty-Elixir self-hosting and downstream dogfooding release. It expands structural Rust generation, improves type-driven lowering, and fixes a major compile-time performance regression exposed by Skia dogfood.

Added

  • Add Rust type item generation from @type aliases, including type aliases, structs, enums, and Rust-only support records that do not force generated Rustler term decoders.
  • Add R.enum(...) for explicit Rust enum type items and fix zero-arity type alias resolution for bare @type name :: ... declarations.
  • Add structural macro_rules! AST support and native validation/rendering for macro variables, captures, repetitions, and macro repeat expressions.
  • Add AST walking utilities for RustQ AST traversal and mutation analysis.
  • Add golden corpus tooling for Rusty-Elixir lowering, with coverage reporting by category.
  • Add semantic enum_variant(Type, :variant, ...) lowering for constructing Rust enum variants from Rusty-Elixir without token macros.
  • Add semantic item-producing defrustmacro calls and macro item call support.
  • Add defrustmacro support for compact skip_fields and sparse struct field descriptor row patterns.
  • Add idiomatic Clippy lint paths for Rust-facing attributes, such as @allow Clippy.redundant_field_names rendering to #[allow(clippy::redundant_field_names)].
  • Add dev-only RustQ Reach smell checks and dogfood them in strict mode for raw Rust escapes, low-level Rusty-Elixir control flow, trivial defrust wrappers, and blockless defrustmod aliases.

Changed

  • Extend defrustmacro with identifier/literal captures, item-generating inner defrust bodies, multiple generated item bodies, and repeat ... do macro-template repetitions.
  • Lower calls to known defrustmacro helpers as generated Rust macro invocations, and support remote Rust macro calls such as Debug.trace!(value).
  • Replace ad hoc macro metavariable spacing cleanup with structural macro repeat expression rendering.
  • Remove the process dictionary from Rusty-Elixir lowering; lowering state is now threaded explicitly through expression, call-argument, alias, array, control-flow, return, and expected-expression lowering.
  • Make context-aware expression lowering the primary lowering path.
  • Expand type-driven checking for expected expression positions, including returns, call arguments, struct fields, case arms, if branches, with/for reduce bodies, closures, field access, statics, array literals, and option some(...) wrappers.
  • Infer downstream types through value uses such as comparisons, binary_search_by_key, receiver method calls, mutable Vec::push, option case bindings, option adapters, package free functions, and Self-based impl callables.
  • Import free functions from configured Cargo packages in addition to impl methods.
  • Resolve Self types in impl callable metadata to the concrete impl target.
  • Keep __rustq_callables__/0 exports limited to local RustQ callables instead of embedding all external/package callables into every module. External callables are still available during compilation through cached source/package resolution.
  • Document item-generating defrustmacro patterns for compact generated Rust that keeps implementation logic in Rusty-Elixir.
  • Document when to use ordinary Elixir defmacro for authoring-layer reuse versus defrustmacro for intentionally reducing generated Rust size.

Fixed

  • Parse Rust bare function pointer types structurally through syn metadata instead of regexing rendered Rust type strings.
  • Mark function pointer types as non-decodable where Rustler decoding cannot be derived safely.
  • Auto-borrow binary-search keys, array literals expected as slices, call arguments, option some(...) inners, option adapter inners, external statics, and several downstream value-use patterns.
  • Avoid applying ? to Option<T> values in Result/NifResult contexts where Rust does not support that propagation boundary.
  • Infer external static types and generated static types for auto-borrowing.
  • Preserve explicit R.raw(...) as an escape hatch while using structured syn metadata where available.
  • Fix zero-cost package metadata reuse after Skia dogfood: generated checks that previously ballooned to roughly 75–80 seconds now avoid duplicating thousands of external callables into every module.

v0.8.3 - 2026-06-28

  • Infer propagation for source-backed receiver method calls when the receiver type is known.
  • Index Rust source callables by normalized receiver target names.
  • Strengthen metadata guidance for avoiding unwrap! when Rust source metadata can be configured.

v0.8.2 - 2026-06-26

  • Add the RustQ agent skill to the Hex package and documentation.
  • Add guidance for authoring RustQ generators with defrust, AST builders, metadata, inference, and explicit escape boundaries.
  • Document expression-oriented Rusty-Elixir style and recursion/reducer patterns.

v0.8.1 - 2026-06-25

  • Add Rustler fixed struct term helpers for cached keys, default values, and raw NIF_TERM map construction.
  • Use Atom::from_str for generated cached atom helpers.
  • Add missing Rust integer marker types to RustQ.Type.

v0.8.0 - 2026-06-25

  • Add semantic control-flow lowering for expression-oriented Rusty-Elixir.
  • Support macro-generated case clauses in defrust lowering.

v0.7.0 - 2026-06-25

  • Add ok_or!(option_expr, error_expr) as the Rusty-Elixir idiom for explicit Option<T> to Result<T, E> propagation boundaries.
  • Extend type-driven propagation inference with nested Rust module function metadata, downstream receiver-method let inference, as_slice() adapters, source-backed From<A> for B compatibility for impl Into<B>, and associated-type metadata such as impl IntoIterator<Item = T>.
  • Add source-backed callable metadata from Rust source files, Cargo packages, and callable modules.
  • Dogfood native RustQ AST generation in Rustler atom dispatch, tagged enum, macro item, and native codegen helper paths.
  • Add structured diagnostics for RustQ configuration, source loading, lowering, and native rendering failures.

v0.6.0 - 2026-06-19

  • Reframe Rusty Elixir as the high-level defrust authoring surface.
  • Add external Rust type specs through ordinary remote types such as GeneratedOpts.OvalOpts.t(RustQ.Type.lifetime(:a)).
  • Add ordinary Elixir macro expansion before defrust lowering so reusable Rusty-Elixir body fragments can use defmacro, quote, and unquote.
  • Lower plural alias calls such as Atoms.fill() to snake-case Rust module calls such as atoms::fill().
  • Demote RustQ.Meta.quoted and RustQ.Type.path to low-level escape hatches instead of the normal authoring style.
  • Add small AST reuse bridges for RustQ-owned codegen helpers: RustQ.Rust.ast_item/1, RustQ.Rust.ast_items/1, and the internal RustQ.Meta.item(module, name) / items(module, names) / ast!(module, name) helpers.
  • Add Rust AST support for receiver arguments and lifetime-bearing impl blocks, including Rustler shapes such as impl<'a> rustler::Decoder<'a> for Type and fn encode<'a>(&self, env: rustler::Env<'a>) -> rustler::Term<'a>.
  • Dogfood Rustler helper generation through defrust and RustQ AST builders: term helpers, opts helpers, term builders, cached atoms, atom decoders, tagged enum decoders/encoders, resources, and term decoder shells no longer rely on hand-written helper templates. Explicit raw NIF_TERM builders remain the unsafe escape hatch.

v0.5.1 - 2026-06-15

  • Add generic Rust expression builders:
    • Rust.call_expr/3
    • Rust.some/1
    • Rust.none/0
    • Rust.tuple/1
    • Rust.cast/2
    • Rust.question/1
    • Rust.ref_expr/2

v0.5.0 - 2026-06-15

  • Add template includes.
  • Add composable RustQ splice groups.
  • Use plain keyword splices for composition.
  • Add structured include diagnostics.
  • Add rustfmt option.
  • Add generic Rust body/control-flow builders:
    • Rust.block/1
    • Rust.let_/2
    • Rust.let_mut/2
    • Rust.assign/2
    • Rust.call_stmt/3
    • Rust.return_if/2
    • Rust.if_/3
    • Rust.if_let/4
    • Rust.match_/2
  • Document macro token placeholder limitation.

v0.4.0 - 2026-06-06

  • Add generic Rustler builders for atom decoders, atom dispatch functions, and keyword/options helper functions.

v0.3.0 - 2026-06-06

  • Remove the generic Rustler schema field group DSL. Prefer explicit fields in RustQ schemas or project-specific macros for domain shorthand.

v0.2.2 - 2026-06-03

  • Add RustQ.Rustler.resource_handle/2 for generating a Rustler resource plus a decoder for Elixir-facing resource handle structs/maps.
  • Add Rustler schema field groups for reusable field sets with use_fields/1.
  • Let Rustler schema nodes override generated Rust type names and Elixir module names with rust: and module:.

v0.2.1 - 2026-06-03

  • Add RustQ.Rustler.nif_export/2 and nif_exports/1 for generating exported Rustler NIF functions that delegate to handwritten implementation functions.

v0.2.0 - 2026-06-02

  • Replace the separate __expr_, __type_, and __splice_ placeholder prefixes with one visually distinct __rq_ placeholder prefix.
  • Templates now use forms like __rq_Name, __rq_value!(), __rq_fields: (),, and __rq_items!();.

v0.1.2 - 2026-06-02

  • Let Rustler schema field types reference schema nodes and tagged enums by schema name, so examples can use Content instead of generated Rust names like ExContent.

v0.1.1 - 2026-06-02

  • Keep the packaged NIF crate out of parent Cargo workspaces when RustQ is used inside workspace-based projects.

v0.1.0 - 2026-06-02

Initial release.

  • Parse, validate, render, bind, and splice Rust templates from Elixir.
  • ~R sigil for inline Rust templates.
  • Rust fragment builders for functions, structs, enums, impls, fields, constants, uses, modules, and type aliases.
  • Rustler helper generators for atoms, NIFs, resources, option decoders, term helpers, term decoders, NIF structs, tagged enums, cached atoms, safe term builders, and explicit raw NIF_TERM builders.
  • rustq.exs manifest DSL plus mix rustq.gen for generated file syncing and stale checks.
  • Rustler schema DSL for generating Rust NIF structs and tagged enums from Elixir schema definitions.