Specifications on FnOnce
For any function object, i.e., a value of any type that implements FnOnce
(for example, a named function, or a closure) the signature can be reasoned about generically
via the Verus built-in functions call_requires
and call_ensures
.
call_requires(f, args)
is a predicate indicating iff
is safe to call with the givenargs
. For any non-static call, Verus requires the developer to prove thatcall_requires(f, args)
is satisfied at the call-site.call_ensures(f, args, output)
is a predicate indicating if it is possible forf
to return the givenoutput
when called withargs
. For any non-static call, Verus will assume thatcall_ensures(f, args, output)
holds after the call-site.- At this time, the
opens_invariants
aspect of the signature is not treated generically. Verus conservatively treats any non-static call as if it might open any invariant.
The args
is always given as a tuple (possibly a 0-tuple or 1-tuple).
See the tutorial chapter for examples and more tips.
For any function with a Verus signature (whether a named function or a closure), Verus generates axioms resembling the following:
(user-declared requires clause) ==> call_requires(f, args)
call_ensures(f, args, output) ==> (user-declared ensures clauses)
Using implication (==>
) rather than a strict equivalence (<==>
) in part to allow
flexible signatures in traits.
However, our axioms use this form for all functions, not just trait functions.
This form reflects the proper way to write specifications for higher-order functions.