Add a Rust language generator - #56
Merged
Merged
Conversation
Rust is the sixth target and the first with a real answer to most of what the AST says rather than a convention standing in for one: - data goes in a `struct` and behaviour in an `impl` block - an interface is a `trait`, and a base type on one is a supertrait - a destructor is `impl Drop`, an operator is its `std::ops` trait, a conversion is `impl From`, and a specialisation is `impl Trait for Type` - the one place a target answers C++'s explicit specialisation exactly - an enumeration's variants are scoped by it, so nothing needs prefixing, and a fixed underlying type is `#[repr]` - a pure function is `#[must_use]`, one fixed at build time is a `const fn`, and an assertion is `const _: () = assert!(...)` - `IsReadOnly` decides the receiver: `&self` against `&mut self`, which is the promise C++ spells as a trailing `const`, stated more strongly What is left over is inheritance, which Rust does not have and which becomes a field with a note, and the operators Rust supplies from another one and will not let a type define by itself - `!=`, the orderings, and the short-circuiting pair - each written as a note naming the trait to implement instead. Two rules are the opposite of every other target here and so are written out: a `let` binds immutably, so an ordinary declaration is `let mut`; and an inferred declaration writes no type at all, which needs no `auto` and no `var`. `RustGeneratedSourceCompilesTests` compiles what it writes with rustc. `CompiledExemplar` is the AST it and the C test are both checked against - one AST, two real compilers, which says more than two parallel fixtures could. The braced-list writer moves from `CFamilyGenerator` up to `StandardLanguageGenerator`, parameterised by its delimiters and by how a language says which member an element is for, so Rust's struct literal and array literal share it rather than copying it. `CGenerator` now says something about a specialisation instead of dropping it silently, which is what the other targets already did. `Coder.Editor` registers a Rust definition with the syntax highlighter, which ships fifteen languages and has no Rust among them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D7Exu6cR3QLkHY5F8uSks7
SonarCloud's S1192: the sentence naming PartialOrd was written four times, once per ordering, and the one naming nothing twice. They are four and two spellings of one decision, so they are one constant each - and the wording now has one place to change rather than six. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D7Exu6cR3QLkHY5F8uSks7
|
This was referenced Sep 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Adds
RustGenerator, a sixth target alongside C#, C++, C, Python and JavaScript.C was interesting because it had almost nothing to map onto. Rust is interesting for the opposite reason: it has a real feature for nearly everything the AST says, so the question is which one each part of a declaration became rather than what to write in its place.
ClassDeclarationstructfor the data and animplblock for the behaviour — the split Rust insists on&selfor&mut selfdepending onIsReadOnly, which is the promise C++ spells as a trailingconst, stated more stronglypub fn new(…) -> Self, built from the initialiser list as the tail expression, with the field-init shorthand where a field takes a variable of its own nameimpl Drop— the one member whose name Rust fixes, and which cannot live in the inherent blocktrait;BaseTypeon one is a supertraitstd::opstrait (impl Add for Point, naming theOutput), orPartialEqfor==impl From<Point> for f64— implementing one givesIntofor freeimpl Describe for Point— the one place a target answers C++'stemplate<> struct Describe<T>exactly, since attaching facts to a type without touching the type is what both are for#[repr(i32)]for a fixed underlying typeIsPure/MustUseResult#[must_use], and nothing on a function that answers nothingIsCompileTimeEvaluableconst fnCompileTimeAssertionconst _: () = assert!(…), which needs no macro cratepub modWhat is left over. Inheritance, which Rust does not have — a base type on a struct becomes a field with a note. And the operators Rust supplies from another one and will not let a type define by itself:
!=comes fromPartialEq, the four orderings fromPartialOrd::partial_cmp, and&&/||short-circuit and are not overloadable at all. Each is written as a note naming the trait to implement instead, rather than as an implementation the language would reject.Two rules are the opposite of every other target, so they are written out rather than inherited. A
letbinds immutably, so an ordinary declaration islet mutand only a constant one is plainlet— the warning an unneededmutearns beats the error a missing one causes. And an inferred declaration writes no type at all, which is the one thing on the list Rust does better than the languageIsTypeInferredwas modelled on: noauto, novar.Testing
RustGeneratorTests— 36 tests pinning which feature each part of a declaration became.RustGeneratedSourceCompilesTests— compiles the output withrustc. Rust earns this more than C did: a receiver that should have been&mut self, an associated item carrying a visibility Rust forbids, a trait implementation missing itsOutput— each is a spelling a test can pin and only a compiler notices. I verified it has teeth by removing thetype Outputline from generated output and watchingrustcreject it. Inconclusive where no toolchain is on the path.CompiledExemplar— one AST, two real compilers. The C and Rust compile tests are now checked against the same declarations rather than parallel fixtures, which is a stronger claim: that the same AST comes out as valid source in each target. Each test still adds what only its language has to answer.Changes outside the new generator
CFamilyGeneratorup toStandardLanguageGenerator, parameterised by its delimiters and by how a language says which member an element is for. Rust's struct literal and array literal share it instead of copying it; C and C++ output is unchanged.CGeneratornow writes a note for a specialisation instead of dropping it silently — one line, and what every other target already did. It was a pre-existing gap, and I hit it because the documentation sentence about specialisations needed rewriting either way.Coder.Editorregisters a Rust definition with the syntax highlighter. It ships fifteen languages and Rust is not among them, so the preview pane would have drawn generated Rust as plain text — the existingEveryGenerator_ProducesSourceTheHighlighterClassifiestest caught exactly that.README,
CLAUDE.mdanddocs/design.mdare updated.🤖 Generated with Claude Code
https://claude.ai/code/session_01D7Exu6cR3QLkHY5F8uSks7
Generated by Claude Code