Return and yield
Use return to finish a function and provide its result. Use yield when a
function should produce a sequence one item at a time.
The return keyword exits a function, lambda, or property accessor. Because
Raven supports both statement and expression forms:
- Statement form:
returnorreturn <expression>in statement position. - Expression form:
return <expression>in expression contexts where early exit is valid (for example,name ?? return -1).
return statements remain statement-only. The compiler reports RAV1900 when a
statement return is used in inline expression contexts (for example, expression
if/match arms).
return expression form is intentionally narrow. It models an abrupt exit from
the enclosing callable while still fitting expression-oriented operators like
??, if arms, and similar value positions. It does not turn expression blocks
into statement-flow regions. In other words:
let b = if (a) e else return -1is valid (else return -1is a return expression).let b = if (a) e else { return -1 }is invalid because{ ... }here is a block expression, andreturninside that block is parsed as a statement.
A return statement may omit its expression when the surrounding function or
accessor returns unit. See implementation notes
for how such returns are emitted. This is equivalent to returning the () value
explicitly.
return // equivalent to returning ()
return () // explicit unit return
Within a method-like body, each return is validated against the declared
return type of that body. A return without an expression is treated as
returning unit. If the returned expression's type is not assignable to the
declared return type, the compiler emits a conversion diagnostic.
Return expressions follow the same return-type validation as return statements: the payload must be assignable to the enclosing function/lambda/accessor return type.
Implicit return inference for methods/functions/lambdas uses explicit return
statements and the outer body tail expression only. Tail expressions inside nested
statement blocks are not treated as implicit returns for the enclosing member.
func DoOperation(a: int, b: int) -> int {
if a > b {
return -1
}
return a + b
}
func DoOperation2(a: int, b: int) -> Result<int, bool> {
if a > b {
return Error(false)
}
return Ok(a + b)
}
Property accessors follow the same rule: each explicit return must produce a
value compatible with the property's declared type.
func choose(flag: bool) -> Result<int, ()> {
return if flag {
Ok(42)
} else {
Error(())
}
}
if flag {
return 42 // allowed: expression used as a statement
}
func log(msg: string) {
Console.WriteLine(msg)
return // equivalent to returning ()
}
func firstCharOrFail(name: string?) -> Result<int, string> {
val required = name ?? return Error("Missing name")
return Ok(required.Length)
}
yield and yield return statements
Iterator-like members may suspend execution with either yield expression or
yield return expression. Each form produces the next element in the
enumerator sequence while preserving the generator's state so execution can
resume on the next iteration. Async iterators returning IAsyncEnumerable<T>
or IAsyncEnumerator<T> also participate in enumeration cancellation through
GetAsyncEnumerator(CancellationToken). To make that enumerator token visible
inside the iterator body, mark the intended CancellationToken parameter with
[EnumeratorCancellation].
func numbers() -> IEnumerable<int> {
var i = 0
while i < 3 {
yield i
i += 1
}
}
import System.Collections.Generic.*
import System.Runtime.CompilerServices.*
import System.Threading.*
import System.Threading.Tasks.*
async func numbers([EnumeratorCancellation] cancellationToken: CancellationToken) -> IAsyncEnumerable<int> {
yield 1
await Task.Delay(1000, cancellationToken)
yield 2
}
yield expression is shorthand for yield return expression. Raven continues
to support the explicit yield return spelling as well. Like explicit return
statements, both yield-value forms are limited to statement positions and
cannot appear in expression context.
yield break statements
yield break terminates an iterator early. Any remaining statements in the
member are skipped, and the enumerator completes without producing additional
values.
func firstOrNone(values: IEnumerable<int>) -> IEnumerable<int> {
for value in values {
yield value
yield break
}
yield break
}
The yield break form follows the same placement rules as yield return and
break—it must appear in statement position and is illegal outside iterator
bodies.