Errors and refusals
RCE has no exceptions. A step either finishes or is refused, and a function either returns a value or fails with a declared error.
Refusing an event
intent "a bank account that refuses overdrafts"
example Deposit(amount: 50), Withdraw(amount: 20) -> 30
example Withdraw(amount: 1) -> refused: Insufficient
state Account
balance: i64 = 0
event Deposit(amount: i64)
event Withdraw(amount: i64)
error Insufficient(need: i64)
error NotPositive
struct Withdrawn
amount: i64
step Account on Deposit(amount: a)
if a <= 0
fail NotPositive
balance += a
step Account on Withdraw(amount: a)
if a > balance
fail Insufficient(need: a - balance)
balance -= a
emit Withdrawn(amount: a)
error Insufficient(need: i64)declares an error with a payload.fail Insufficient(need: a - balance)refuses the event with that error.emit Withdrawn(amount: a)produces an output value as part of the step.event Deposit(amount: i64)has a payload, and the step's patternDeposit(amount: a)names ita.
$ rce run account.rce "Deposit(amount: 50), Withdraw(amount: 20), Withdraw(amount: 40)"
0 (initial) Account { balance = 0 }
1 Deposit(amount: 50) Account { balance = 50 }
2 Withdraw(amount: 20) Account { balance = 30 }
emit Withdrawn(amount: 20)
3 Withdraw(amount: 40) refused: Insufficient
exit status: 1
The third event asks for more than the balance, so it is refused and the balance stays at 30.
Functions that can fail
A function that can fail says so in its return type. u8 ! NotADigit
means "a u8, or the error NotADigit".
error NotADigit(at: u32)
fn digit(c: text, at: u32) -> u8 ! NotADigit
if c.isdigit()
return c.toint() as u8
fail NotADigit(at: at)
fn sum_digits(s: text) -> u32 ! NotADigit
var total: u32 = 0
var i: u32 = 0
for c in s.chars()
total += (try digit(c, i)) as u32
i += 1
return total
fn sum_or_zero(s: text) -> u32 = sum_digits(s) catch 0
fn where_it_broke(s: text) -> u32 = sum_digits(s) catch |e| e.at
check sum_or_zero("123") == 6
check sum_or_zero("1x3") == 0
check where_it_broke("12x") == 2
fail NotADigit(at: at)returns the error from the function.try digit(c, i)uses the value if there is one, and otherwise passes the error on to the caller. That is whysum_digitsalso has! NotADigitin its type.sum_digits(s) catch 0turns an error into a default value.catch |e| e.atgives the error a name so you can use its payload.
A value of type u32 ! NotADigit is not a u32. The checker refuses code
that uses it as one until the error is handled with try or catch, so an
error cannot be ignored by accident. A step whose code fails with an error
refuses the event.
Traps
A trap is a failure that is not a declared error: an integer that does
not fit, a division by zero, a loop that reaches its limit. A trap inside a
step refuses the event, exactly like fail. A trap in a check or an
example makes the program inadmissible, as you saw in chapters 2 and 3.
What you learned
error,failand refused events.- Error unions
T ! E, withtry,catchandcatch |e|. - Traps refuse an event in the same way an error does.