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
{
Dessert::new(/*...*/)
}
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
}