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
Copy file name to clipboardExpand all lines: docs/language/control-flow.md
+164Lines changed: 164 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -103,6 +103,170 @@ if let number = noNumber {
103
103
}
104
104
```
105
105
106
+
## Guard statement
107
+
108
+
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`).
109
+
110
+
The Cadence type checker enforces this: a guard whose `else` block can fall through is a type error.
111
+
112
+
### Basic boolean guard
113
+
114
+
```cadence
115
+
fun divide(_ a: Int, by b: Int): Int {
116
+
guard b != 0 else {
117
+
return 0
118
+
}
119
+
return a / b
120
+
}
121
+
122
+
divide(10, by: 2) // returns 5
123
+
divide(10, by: 0) // returns 0
124
+
```
125
+
126
+
### Comparison with if-statement
127
+
128
+
`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:
129
+
130
+
```cadence
131
+
// Using if — validation logic nests deeper
132
+
fun processAge(_ age: Int): String {
133
+
if age >= 0 {
134
+
if age <= 150 {
135
+
return "Valid age: \(age)"
136
+
} else {
137
+
return "Too old"
138
+
}
139
+
} else {
140
+
return "Negative age"
141
+
}
142
+
}
143
+
144
+
// Using guard — happy path stays flat
145
+
fun processAge(_ age: Int): String {
146
+
guard age >= 0 else {
147
+
return "Negative age"
148
+
}
149
+
guard age <= 150 else {
150
+
return "Too old"
151
+
}
152
+
return "Valid age: \(age)"
153
+
}
154
+
```
155
+
156
+
### Optional binding with guard
157
+
158
+
`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.
159
+
160
+
```cadence
161
+
fun greet(_ name: String?): String {
162
+
guard let unwrappedName = name else {
163
+
return "Hello, stranger"
164
+
}
165
+
// `unwrappedName` is available here, already unwrapped to `String`
166
+
return "Hello, \(unwrappedName)"
167
+
}
168
+
169
+
greet("Alice") // returns "Hello, Alice"
170
+
greet(nil) // returns "Hello, stranger"
171
+
```
172
+
173
+
Contrast with `if let`:
174
+
175
+
```cadence
176
+
fun greet(_ name: String?): String {
177
+
if let unwrappedName = name {
178
+
// `unwrappedName` only exists inside this block
179
+
return "Hello, \(unwrappedName)"
180
+
}
181
+
return "Hello, stranger"
182
+
// `unwrappedName` is NOT available here
183
+
}
184
+
```
185
+
186
+
Use `guard var` when the unwrapped value needs to be mutated after the guard:
187
+
188
+
```cadence
189
+
fun normalize(_ value: Int?): Int {
190
+
guard var n = value else {
191
+
return 0
192
+
}
193
+
if n < 0 {
194
+
n = 0
195
+
}
196
+
return n
197
+
}
198
+
```
199
+
200
+
### Chaining guards
201
+
202
+
Multiple guards at the top of a function express preconditions clearly, without nesting:
203
+
204
+
```cadence
205
+
struct User {
206
+
let name: String
207
+
let age: Int
208
+
209
+
init(name: String, age: Int) {
210
+
self.name = name
211
+
self.age = age
212
+
}
213
+
}
214
+
215
+
fun createUser(name: String?, age: Int?): User? {
216
+
guard let validName = name else { return nil }
217
+
guard let validAge = age else { return nil }
218
+
guard validName.length > 0 else { return nil }
219
+
guard validAge >= 0 else { return nil }
220
+
221
+
// All checks passed; both `validName` and `validAge` are in scope
222
+
return User(name: validName, age: validAge)
223
+
}
224
+
```
225
+
226
+
### Guard in loops
227
+
228
+
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:
251
+
252
+
```cadence
253
+
resource Vault {
254
+
let balance: UFix64
255
+
init(balance: UFix64) {
256
+
self.balance = balance
257
+
}
258
+
}
259
+
260
+
fun withdraw(_ vault: @Vault?): UFix64 {
261
+
guard let v <- vault else {
262
+
return 0.0
263
+
}
264
+
let balance = v.balance
265
+
destroy v
266
+
return balance
267
+
}
268
+
```
269
+
106
270
## Switch
107
271
108
272
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