4. Control flow¶
Control flow is how you branch and loop.
if / elif / else¶
def describe(n: int) -> str:
if n < 0:
return "negative"
elif n == 0:
return "zero"
else:
return "positive"
Use ordinary if when the condition is a boolean expression.
if let (do something only when one pattern matches)¶
Use if let when you care about exactly one successful pattern and want the non-match case to do nothing.
def greet(user: Option[User]) -> None:
if let Some(u) = user:
println(f"hello {u.name}")
This is shorter than a full match when the only interesting case is the successful one.
def greet(user: Option[User]) -> None:
match user:
case Some(u): println(f"hello {u.name}")
case None: pass
Use match instead when both branches matter. In v1, if let is single-arm only and does not accept elif or else.
match (pattern matching)¶
match is the main way to branch on enums like Result and Option:
def main() -> None:
result = parse_port("8080")
match result:
case Ok(port): println(f"port={port}")
case Err(e): println(f"error: {e}")
Coming from Rust?
Incan also supports a more Rust-like match-arm style using =>:
def main() -> None:
match parse_port("8080"):
Ok(port) => println(f"port={port}")
Err(e) => println(f"error: {e}")
This is equivalent to the case ...: form; pick whichever reads best to you.
while let (loop while one pattern keeps matching)¶
Use while let when the loop should continue only while one pattern keeps matching.
async def consume(rx: Receiver[str]) -> None:
while let Some(msg) = await rx.recv():
println(f"received {msg}")
This is the compact form of:
async def consume(rx: Receiver[str]) -> None:
while True:
match await rx.recv():
case Some(msg): println(f"received {msg}")
case None: break
for loops¶
Incan supports Python-like for loops:
def main() -> None:
items = ["Alice", "Bob", "Cara"]
for name in items:
println(name)
You can break early:
for name in items:
if name == "Bob":
break
while loops¶
Use while when the condition should be re-checked before each iteration:
def countdown(start: int) -> None:
mut current = start
while current > 0:
println(current)
current -= 1
loop: and break <value>¶
Use loop: for explicit infinite loops and for loops that need to return a value:
def find_value(flag: bool) -> int:
return loop:
if flag:
break 42
break 7
break <value> completes the surrounding loop: expression. For for and while, use plain break.
Try it¶
- Write a function
classify(n: int) -> strusingif/elif/else. - Use
if leton anOption[User]and print the user's name only when present. - Use
matchon aResultand print either the value or the error. - Write a
while letloop that consumes messages until a channel closes. - Loop over a list and stop early with
break. - Write a
loop:expression that returns anintwithbreak <value>.
One possible solution
# 1) classify function
def classify(n: int) -> str:
if n < 0:
return "negative"
elif n == 0:
return "zero"
else:
return "positive"
def main() -> None:
println(classify(-1)) # negative
println(classify(0)) # zero
println(classify(2)) # positive
# 2) if let on Option
maybe_name = Some("Danny")
if let Some(name) = maybe_name:
println(name)
# 3) match on Result
match parse_port("8080"):
Ok(port) => println(f"port={port}")
Err(e) => println(f"error={e}")
# 4) while let on a sequence of optional values
def next_value(values: list[Option[int]], idx: int) -> Option[int]:
if idx < len(values):
return values[idx]
return None
values = [Some(1), Some(2), None]
idx = 0
while let Some(value) = next_value(values, idx):
println(value)
idx += 1
# 5) loop over a list and stop early with break
items = ["Alice", "Bob", "Cara"]
for name in items:
if name == "Bob":
break
println(name)
# 6) loop expression with break value
value = loop:
if len(items) > 0:
break 42
break 0
println(value)
Where to learn more¶
- Control flow overview: Control flow
- Enums (often used with
match): Enums - Error handling (deep dive on
Result/Option): Error Handling
Next¶
Back: 3. Functions
Next chapter: 5. Modules and imports