Skip to content

Commit a21a81e

Browse files
committed
Make verified-patterns.md the canonical idiom reference and slim ION_SPEC §9/§12 to link to it.
1 parent e88c8ec commit a21a81e

5 files changed

Lines changed: 172 additions & 196 deletions

File tree

.cursor/skills/ion-lang/SKILL.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Ion is a move-only, no-GC systems language transpiled to C. This skill orients y
2525
| [CHANGELOG.md](../../../CHANGELOG.md) | Release notes by month |
2626
| [SECURITY.md](../../../SECURITY.md) | Security reporting scope and policy |
2727
| [tests/README.md](../../../tests/README.md) | Integration test catalog |
28+
| [writing-ion-code/references/verified-patterns.md](../writing-ion-code/references/verified-patterns.md) | Canonical idioms (ION_SPEC §12 indexes this) |
2829

2930
**Compiler pipeline** (see [references/compiler-pipeline.md](references/compiler-pipeline.md)):
3031

.cursor/skills/ion-lang/references/language-constraints.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ References `&T` and `&mut T` are stack-local views. **Rejected:**
2121

2222
**Borrow conflicts** (ION_SPEC section 5.3): at most one `&mut T` or any number of `&T` on the same **root owner binding**. Lasting borrows come from `let r = &x`, `let r = &mut s.field`, etc., and end when the binding's scope ends; field and index paths borrow the root owner, not disjoint field slots. Ephemeral `&` / `&mut` in call arguments are checked but not stored. While a lasting borrow is active, the owner cannot be read, assigned, or moved (including copy types and other field paths).
2323

24-
APIs that would return `&T` in Rust must use owned values, indices, or callbacks in Ion.
24+
APIs that would return `&T` in Rust must use owned values, indices, or the patterns in [writing-ion-code/references/verified-patterns.md](../writing-ion-code/references/verified-patterns.md).
2525

2626
## Concurrency
2727

.cursor/skills/writing-ion-code/SKILL.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Task progress:
3232

3333
For ownership and no-escape rules, see [ion-lang/references/language-constraints.md](../ion-lang/references/language-constraints.md).
3434

35-
For syntax templates and anti-patterns, see [references/verified-patterns.md](references/verified-patterns.md).
35+
For syntax templates and verified idioms, see [references/verified-patterns.md](references/verified-patterns.md) (canonical; ION_SPEC §12 indexes it).
3636

3737
## Program workflow
3838

@@ -133,7 +133,7 @@ Fn literals lower to static C functions and must not reference outer bindings (o
133133

134134
## Documentation comments
135135

136-
Ion uses plain `//` line comments only. No `///` or `//!`. Contiguous `//` lines **immediately above** a declaration (no blank line before the declaration) are attached as documentation for IDE hover; the same rule applies when `pub` precedes the item. A blank line breaks association. File-level overview comments go at the top of the file before imports. Section-divider comment blocks in examples should be separated from declarations by a blank line. See ION_SPEC section 12.6.
136+
Ion uses plain `//` line comments only. No `///` or `//!`. Contiguous `//` lines **immediately above** a declaration (no blank line before the declaration) are attached as documentation for IDE hover; the same rule applies when `pub` precedes the item. A blank line breaks association. File-level overview comments go at the top of the file before imports. Section-divider comment blocks in examples should be separated from declarations by a blank line. See ION_SPEC §12.1.
137137

138138
**Unsafe and FFI**
139139

@@ -187,7 +187,7 @@ These are **not** in Ion today. Check ION_SPEC.md section 10.3 before using anyt
187187
- Union types `A | B` (reserved; use enums)
188188
- Nested tuples, tuple `==`, or generic tuple type parameters
189189
- `mut` on function parameters (use `&mut T` in the signature instead)
190-
- `///` / `//!` doc comment syntax (use adjacent `//` instead; see ION_SPEC §12.6)
190+
- `///` / `//!` doc comment syntax (use adjacent `//` instead; see ION_SPEC §12.1)
191191
- File APIs beyond `fs::read_to_string_result` (streaming `File` is deferred in spec)
192192

193193
When unsure, **grep** `tests/` and `examples/` for the construct. If nothing matches, tell the user it is likely unsupported rather than inventing syntax.

.cursor/skills/writing-ion-code/references/verified-patterns.md

Lines changed: 146 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Verified Ion Patterns
22

3+
**Canonical idiom reference** for agents and authors. [ION_SPEC.md](../../../ION_SPEC.md) §12 indexes this file; keep long examples here, not in the spec. Every `ion` block should match a pattern in `tests/` or `examples/` (linked inline). When semantics change, update this file and tests together.
4+
35
Copy patterns from here or from linked test files. Do not add constructs not shown in ION_SPEC.md or this repo.
46

57
## Types
@@ -62,6 +64,147 @@ fn step(vm: &mut VM) {
6264

6365
Field paths are valid assignment targets on owned structs and `&mut` parameters.
6466

67+
## Ownership move
68+
69+
```ion
70+
struct Packet {
71+
id: i32;
72+
data: Vec<u8>;
73+
}
74+
75+
fn make_packet(data: Vec<u8>) -> Packet {
76+
Packet { id: 1, data: data }
77+
}
78+
79+
fn main() -> int {
80+
let buf: Vec<u8> = Vec::new();
81+
let pkt = make_packet(buf);
82+
// buf is invalid after the move; pkt owns the data
83+
return 0;
84+
}
85+
```
86+
87+
See [tests/test_move_basic.ion](../../../../tests/test_move_basic.ion).
88+
89+
## Index and handle search
90+
91+
When another language would return `&T`, return an index (`int`) or handle and re-index locally. Read-only scans use `Vec::get_ref` (does not hollow the vector). See [tests/test_vec_search_index_ok.ion](../../../../tests/test_vec_search_index_ok.ion) and [tests/test_vec_get_ref_scan.ion](../../../../tests/test_vec_get_ref_scan.ion).
92+
93+
```ion
94+
enum Option<T> {
95+
Some(T);
96+
None;
97+
}
98+
99+
struct Customer {
100+
id: int;
101+
active: int;
102+
}
103+
104+
fn find_customer_index(customers: &Vec<Customer>, target_id: int) -> int {
105+
let mut i: int = 0;
106+
let len: int = Vec::len(customers);
107+
while i < len {
108+
match Vec::get_ref(customers, i) {
109+
Option::Some(c) => {
110+
if c.id == target_id {
111+
return i;
112+
}
113+
}
114+
Option::None => {
115+
return -1;
116+
}
117+
};
118+
i = i + 1;
119+
}
120+
return -1;
121+
}
122+
123+
fn main() -> int {
124+
let mut customers: Vec<Customer> = Vec::new();
125+
let idx: int = find_customer_index(&customers, 42);
126+
if idx >= 0 {
127+
match Vec::get(&customers, idx) {
128+
Option::Some(c) => {
129+
let id: int = c.id;
130+
Vec::set(
131+
&mut customers,
132+
idx,
133+
Customer {
134+
id: id,
135+
active: 1,
136+
},
137+
);
138+
}
139+
Option::None => {}
140+
};
141+
}
142+
return 0;
143+
}
144+
```
145+
146+
## Mutating Vec elements
147+
148+
`Vec::get` moves the element out. Copy fields, rebuild the struct, and `Vec::set` it back ([tests/test_vec_get_putback.ion](../../../../tests/test_vec_get_putback.ion)). Helpers can take `&mut T` on an owned local:
149+
150+
```ion
151+
fn mark_active(c: &mut Customer) {
152+
c.active = 1;
153+
}
154+
```
155+
156+
## Comparing borrowed structs
157+
158+
Read fields through `&Struct` parameters. Ion does not allow reference fields in structs.
159+
160+
```ion
161+
struct Customer {
162+
id: int;
163+
score: int;
164+
}
165+
166+
fn compare(a: &Customer, b: &Customer) -> int {
167+
if a.score != b.score {
168+
return a.score - b.score;
169+
}
170+
return a.id - b.id;
171+
}
172+
```
173+
174+
## Concurrency and ownership transfer
175+
176+
Move owned values into `spawn` and channels; no shared mutable state across threads. See [examples/spawn_channel/spawn_channel.ion](../../../../examples/spawn_channel/spawn_channel.ion) and [examples/channel_worker/channel_worker.ion](../../../../examples/channel_worker/channel_worker.ion).
177+
178+
```ion
179+
struct Job {
180+
data: Vec<u8>;
181+
}
182+
183+
fn worker(rx: Receiver<Job>) {
184+
let mut rx_mut: Receiver<Job> = rx;
185+
loop {
186+
let job: Job = recv(&mut rx_mut);
187+
// use job.data
188+
}
189+
}
190+
191+
fn main() -> int {
192+
let (tx, rx): (Sender<Job>, Receiver<Job>) = channel<Job>();
193+
194+
spawn {
195+
worker(rx);
196+
};
197+
198+
let job = Job { data: Vec::new() };
199+
send(&tx, job);
200+
return 0;
201+
}
202+
```
203+
204+
## Owned API boundaries
205+
206+
At module exports and public functions, prefer owned results (`Vec<T>`, `String`, structs with owned fields) over borrowed views. Use `&T`, `&str`, and local `Option<&T>` from `Vec::get_ref` inside a single function only.
207+
65208
## VM dispatch loop
66209

67210
See [tests/test_vm_execute.ion](../../../../tests/test_vm_execute.ion): `match vm.code.get_ref(vm.ip)` then inner `match op` on `&Op`, field updates, and `break` inside `match` within `loop`.
@@ -193,14 +336,12 @@ let w = v;
193336
let _ = v.len();
194337
```
195338

196-
See [tests/test_ref_return_error2.ion](../../../../tests/test_ref_return_error2.ion), [tests/test_spawn_ref_error.ion](../../../../tests/test_spawn_ref_error.ion), and ION_SPEC.md section 9.4.
339+
See [tests/test_ref_return_error2.ion](../../../../tests/test_ref_return_error2.ion), [tests/test_spawn_ref_error.ion](../../../../tests/test_spawn_ref_error.ion), and ION_SPEC.md §9.4.
197340

198341
## Design alternatives (no borrowed returns)
199342

200343
When another language would return `&T`:
201344

202345
- Return an owned value or `Option<T>` by move
203-
- Return an index or handle, caller re-indexes locally
204-
- Pass a callback that receives `&T` inside the callee's stack frame
205-
206-
See ION_SPEC.md section 12 (non-normative).
346+
- Return an index or handle; caller re-indexes locally (see **Index and handle search** above)
347+
- Call a helper with `&mut T` on an owned local after `Vec::get` / before `Vec::set`

0 commit comments

Comments
 (0)