/// This module defines the Option type and its methods to represent and handle an optional value. module std::option { use std::vector; /// Abstraction of a value that may or may not be present. Implemented with a vector of size /// zero or one because Move bytecode does not have ADTs. struct Option has copy, drop, store { vec: vector } spec Option { /// The size of vector is always less than equal to 1 /// because it's 0 for "none" or 1 for "some". invariant len(vec) <= 1; } /// The `Option` is in an invalid state for the operation attempted. /// The `Option` is `Some` while it should be `None`. const EOPTION_IS_SET: u64 = 0x40000; /// The `Option` is in an invalid state for the operation attempted. /// The `Option` is `None` while it should be `Some`. const EOPTION_NOT_SET: u64 = 0x40001; /// Return an empty `Option` public fun none(): Option { Option { vec: vector::empty() } } spec none { pragma opaque; aborts_if false; ensures result == spec_none(); } spec fun spec_none(): Option { Option{ vec: vec() } } /// Return an `Option` containing `e` public fun some(e: Element): Option { Option { vec: vector::singleton(e) } } spec some { pragma opaque; aborts_if false; ensures result == spec_some(e); } spec fun spec_some(e: Element): Option { Option{ vec: vec(e) } } /// Return true if `t` does not hold a value public fun is_none(t: &Option): bool { vector::is_empty(&t.vec) } spec is_none { pragma opaque; aborts_if false; ensures result == is_none(t); } /// Return true if `t` holds a value public fun is_some(t: &Option): bool { !vector::is_empty(&t.vec) } spec is_some { pragma opaque; aborts_if false; ensures result == is_some(t); } /// Return true if the value in `t` is equal to `e_ref` /// Always returns `false` if `t` does not hold a value public fun contains(t: &Option, e_ref: &Element): bool { vector::contains(&t.vec, e_ref) } spec contains { pragma opaque; aborts_if false; ensures result == spec_contains(t, e_ref); } spec fun spec_contains(t: Option, e: Element): bool { is_some(t) && borrow(t) == e } /// Return an immutable reference to the value inside `t` /// Aborts if `t` does not hold a value public fun borrow(t: &Option): &Element { assert!(is_some(t), EOPTION_NOT_SET); vector::borrow(&t.vec, 0) } spec borrow { pragma opaque; include AbortsIfNone; ensures result == borrow(t); } /// Return a reference to the value inside `t` if it holds one /// Return `default_ref` if `t` does not hold a value public fun borrow_with_default(t: &Option, default_ref: &Element): &Element { let vec_ref = &t.vec; if (vector::is_empty(vec_ref)) default_ref else vector::borrow(vec_ref, 0) } spec borrow_with_default { pragma opaque; aborts_if false; ensures result == (if (is_some(t)) borrow(t) else default_ref); } /// Return the value inside `t` if it holds one /// Return `default` if `t` does not hold a value public fun get_with_default( t: &Option, default: Element, ): Element { let vec_ref = &t.vec; if (vector::is_empty(vec_ref)) default else *vector::borrow(vec_ref, 0) } spec get_with_default { pragma opaque; aborts_if false; ensures result == (if (is_some(t)) borrow(t) else default); } /// Convert the none option `t` to a some option by adding `e`. /// Aborts if `t` already holds a value public fun fill(t: &mut Option, e: Element) { let vec_ref = &mut t.vec; if (vector::is_empty(vec_ref)) vector::push_back(vec_ref, e) else abort EOPTION_IS_SET } spec fill { pragma opaque; aborts_if is_some(t) with EOPTION_IS_SET; ensures is_some(t); ensures borrow(t) == e; } /// Convert a `some` option to a `none` by removing and returning the value stored inside `t` /// Aborts if `t` does not hold a value public fun extract(t: &mut Option): Element { assert!(is_some(t), EOPTION_NOT_SET); vector::pop_back(&mut t.vec) } spec extract { pragma opaque; include AbortsIfNone; ensures result == borrow(old(t)); ensures is_none(t); } /// Return a mutable reference to the value inside `t` /// Aborts if `t` does not hold a value public fun borrow_mut(t: &mut Option): &mut Element { assert!(is_some(t), EOPTION_NOT_SET); vector::borrow_mut(&mut t.vec, 0) } spec borrow_mut { pragma opaque; include AbortsIfNone; ensures result == borrow(t); } /// Swap the old value inside `t` with `e` and return the old value /// Aborts if `t` does not hold a value public fun swap(t: &mut Option, e: Element): Element { assert!(is_some(t), EOPTION_NOT_SET); let vec_ref = &mut t.vec; let old_value = vector::pop_back(vec_ref); vector::push_back(vec_ref, e); old_value } spec swap { pragma opaque; include AbortsIfNone; ensures result == borrow(old(t)); ensures is_some(t); ensures borrow(t) == e; } /// Swap the old value inside `t` with `e` and return the old value; /// or if there is no old value, fill it with `e`. /// Different from swap(), swap_or_fill() allows for `t` not holding a value. public fun swap_or_fill(t: &mut Option, e: Element): Option { let vec_ref = &mut t.vec; let old_value = if (vector::is_empty(vec_ref)) none() else some(vector::pop_back(vec_ref)); vector::push_back(vec_ref, e); old_value } spec swap_or_fill { pragma opaque; ensures result == old(t); ensures borrow(t) == e; } /// Destroys `t.` If `t` holds a value, return it. Returns `default` otherwise public fun destroy_with_default(t: Option, default: Element): Element { let Option { vec } = t; if (vector::is_empty(&mut vec)) default else vector::pop_back(&mut vec) } spec destroy_with_default { pragma opaque; aborts_if false; ensures result == (if (is_some(t)) borrow(t) else default); } /// Unpack `t` and return its contents /// Aborts if `t` does not hold a value public fun destroy_some(t: Option): Element { assert!(is_some(&t), EOPTION_NOT_SET); let Option { vec } = t; let elem = vector::pop_back(&mut vec); vector::destroy_empty(vec); elem } spec destroy_some { pragma opaque; include AbortsIfNone; ensures result == borrow(t); } /// Unpack `t` /// Aborts if `t` holds a value public fun destroy_none(t: Option) { assert!(is_none(&t), EOPTION_IS_SET); let Option { vec } = t; vector::destroy_empty(vec) } spec destroy_none { pragma opaque; aborts_if is_some(t) with EOPTION_IS_SET; } /// Convert `t` into a vector of length 1 if it is `Some`, /// and an empty vector otherwise public fun to_vec(t: Option): vector { let Option { vec } = t; vec } spec to_vec { pragma opaque; aborts_if false; ensures result == t.vec; } spec module {} // switch documentation context back to module level spec module { pragma aborts_if_is_strict; } /// # Helper Schema spec schema AbortsIfNone { t: Option; aborts_if is_none(t) with EOPTION_NOT_SET; } }