You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Forward-ports Bastian Müller's PR #293 — adds a comprehensive 'Guard
statement' section to /docs/language/control-flow with 6 subsections:
basic boolean guard, comparison with if-statement (nested vs flat),
optional binding (guard let / guard var with scope semantics), chaining
guards (multiple preconditions), guard in loops (continue / break),
and guard with resources (move operator interaction).
Inserted between the existing 'Optional binding' and 'Switch' top-level
sections, matching upstream placement. Pure markdown content with
fenced code blocks; no MDX-specific conversion needed.
Brings eval/pr285-cleanup back in sync with origin/main since the
prior fetch (gained 4 commits: ae15a1a, c937619, ebe6298, 08379ac =
PR #293 + 3 fixups). The fixup commits ('use string templates',
'Cadence type checker not compiler') are already incorporated in the
final upstream text used as the source.
Refs: pr285-evaluation/findings.md (Phase 7.1 rebase)
Copy file name to clipboardExpand all lines: content/docs/language/control-flow.mdx
+164Lines changed: 164 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -102,6 +102,170 @@ if let number = noNumber {
102
102
}
103
103
```
104
104
105
+
## Guard statement
106
+
107
+
The guard statement is an early-exit mechanism. It asserts that a condition must be true (or an optional must be non-nil) to continue execution. If the condition is false, the mandatory `else` block runs — and that block **must** exit the current scope via `return`, `break`, `continue`, or a function that never returns (e.g., `panic`).
108
+
109
+
The Cadence type checker enforces this: a guard whose `else` block can fall through is a type error.
110
+
111
+
### Basic boolean guard
112
+
113
+
```cadence
114
+
fun divide(_ a: Int, by b: Int): Int {
115
+
guard b != 0 else {
116
+
return 0
117
+
}
118
+
return a / b
119
+
}
120
+
121
+
divide(10, by: 2) // returns 5
122
+
divide(10, by: 0) // returns 0
123
+
```
124
+
125
+
### Comparison with if-statement
126
+
127
+
`guard` reads as "ensure this is true, otherwise bail out." The normal path stays at the outermost indentation level — there is no rightward drift for validation logic:
128
+
129
+
```cadence
130
+
// Using if — validation logic nests deeper
131
+
fun processAge(_ age: Int): String {
132
+
if age >= 0 {
133
+
if age <= 150 {
134
+
return "Valid age: \(age)"
135
+
} else {
136
+
return "Too old"
137
+
}
138
+
} else {
139
+
return "Negative age"
140
+
}
141
+
}
142
+
143
+
// Using guard — happy path stays flat
144
+
fun processAge(_ age: Int): String {
145
+
guard age >= 0 else {
146
+
return "Negative age"
147
+
}
148
+
guard age <= 150 else {
149
+
return "Too old"
150
+
}
151
+
return "Valid age: \(age)"
152
+
}
153
+
```
154
+
155
+
### Optional binding with guard
156
+
157
+
`guard let` and `guard var` unwrap an optional and — crucially — make the bound variable available **in the enclosing scope**, after the guard. This is the key difference from `if let`, where the bound variable only exists inside the `if` block.
158
+
159
+
```cadence
160
+
fun greet(_ name: String?): String {
161
+
guard let unwrappedName = name else {
162
+
return "Hello, stranger"
163
+
}
164
+
// `unwrappedName` is available here, already unwrapped to `String`
165
+
return "Hello, \(unwrappedName)"
166
+
}
167
+
168
+
greet("Alice") // returns "Hello, Alice"
169
+
greet(nil) // returns "Hello, stranger"
170
+
```
171
+
172
+
Contrast with `if let`:
173
+
174
+
```cadence
175
+
fun greet(_ name: String?): String {
176
+
if let unwrappedName = name {
177
+
// `unwrappedName` only exists inside this block
178
+
return "Hello, \(unwrappedName)"
179
+
}
180
+
return "Hello, stranger"
181
+
// `unwrappedName` is NOT available here
182
+
}
183
+
```
184
+
185
+
Use `guard var` when the unwrapped value needs to be mutated after the guard:
186
+
187
+
```cadence
188
+
fun normalize(_ value: Int?): Int {
189
+
guard var n = value else {
190
+
return 0
191
+
}
192
+
if n < 0 {
193
+
n = 0
194
+
}
195
+
return n
196
+
}
197
+
```
198
+
199
+
### Chaining guards
200
+
201
+
Multiple guards at the top of a function express preconditions clearly, without nesting:
202
+
203
+
```cadence
204
+
struct User {
205
+
let name: String
206
+
let age: Int
207
+
208
+
init(name: String, age: Int) {
209
+
self.name = name
210
+
self.age = age
211
+
}
212
+
}
213
+
214
+
fun createUser(name: String?, age: Int?): User? {
215
+
guard let validName = name else { return nil }
216
+
guard let validAge = age else { return nil }
217
+
guard validName.length > 0 else { return nil }
218
+
guard validAge >= 0 else { return nil }
219
+
220
+
// All checks passed; both `validName` and `validAge` are in scope
221
+
return User(name: validName, age: validAge)
222
+
}
223
+
```
224
+
225
+
### Guard in loops
226
+
227
+
Inside a loop, `guard` can use `continue` or `break` instead of `return`:
`guard let` works with optional resources using the `<-` move operator. The `else` block must consume or destroy the original resource before exiting:
250
+
251
+
```cadence
252
+
resource Vault {
253
+
let balance: UFix64
254
+
init(balance: UFix64) {
255
+
self.balance = balance
256
+
}
257
+
}
258
+
259
+
fun withdraw(_ vault: @Vault?): UFix64 {
260
+
guard let v <- vault else {
261
+
return 0.0
262
+
}
263
+
let balance = v.balance
264
+
destroy v
265
+
return balance
266
+
}
267
+
```
268
+
105
269
## Switch
106
270
107
271
Switch-statements compare a value against several possible values of the same type, in order. When an equal value is found, the associated block of code is executed.
0 commit comments