Enum
In Verus, just as in Rust, you can use enum to define a datatype that is any one of the
defined variants:
enum Beverage {
Coffee { creamers: nat, sugar: bool },
Soda { flavor: Syrup },
Water { ice: bool },
}
An enum is often used just for its tags, without member fields:
enum Syrup {
Cola,
RootBeer,
Orange,
LemonLime,
}
Identifying a variant with the is operator
In spec contexts, the is operator lets you query which variant of an
enum a variable contains.
fn make_float(bev: Beverage) -> Dessert
requires bev is Soda
{
assert(bev !is Coffee);
Dessert::new(/*...*/)
}
The syntax !is is a shorthand for !(.. is ..).
Accessing fields with the arrow operator
If all the fields have distinct names, as in the Beverage example,
you can refer to fields with the arrow -> operator:
proof fn sufficiently_creamy(bev: Beverage) -> bool
requires bev is Coffee
{
bev->creamers >= 2
}
If an enum field reuses a name, you can qualify the field access:
enum Life {
Mammal { legs: int, has_pocket: bool },
Arthropod { legs: int, wings: int },
Plant { leaves: int },
}
spec fn is_insect(l: Life) -> bool
{
l is Arthropod && l->Arthropod_legs == 6
}
match works as in Rust.
enum Shape {
Circle(int),
Rect(int, int),
}
spec fn area_2(s: Shape) -> int {
match s {
Shape::Circle(radius) => { radius * radius * 3 },
Shape::Rect(width, height) => { width * height }
}
}
For variants like Shape declared with round parentheses (),
you can use Verus’ `->’ tuple-like syntax to access a single field
without a match destruction:
spec fn rect_height(s: Shape) -> int
recommends s is Rect
{
s->1
}
matches with &&, ==>, and &&&
match is natural for examining every variant of an enum.
If you’d like to bind the fields while only considering one or two of the
variants, you can use Verus’ matches syntax:
use Life::*;
spec fn cuddly(l: Life) -> bool {
||| l matches Mammal { legs, .. } && legs == 4
||| l matches Arthropod { legs, wings } && legs == 8 && wings == 0
}
Because the matches syntax binds names in patterns, it has no trouble
with field names reused across variants, so it may be preferable
to the (qualified) arrow syntax.
Notice that l matches Mammal{legs} && legs == 4 is a boolean expression,
with the special property that legs is bound in the remainder of the
expression after &&. That helpful binding also works with ==>
and &&&:
spec fn is_kangaroo(l: Life) -> bool {
&&& l matches Life::Mammal { legs, has_pocket }
&&& legs == 2
&&& has_pocket
}
spec fn walks_upright(l: Life) -> bool {
l matches Life::Mammal { legs, .. } ==> legs == 2
}