Skip to content

Commit

Permalink
Result enhancements (FuelLabs#3608)
Browse files Browse the repository at this point in the history
Enhanced `Result` documentation and add `unwrap_ok` method.
  • Loading branch information
jtriley-eth authored Dec 20, 2022
1 parent 495ad00 commit de27066
Show file tree
Hide file tree
Showing 4 changed files with 320 additions and 30 deletions.
126 changes: 126 additions & 0 deletions sway-lib-std/src/result.sw
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,55 @@
//! errors. It is an enum with the variants, [`Ok(T)`], representing
//! success and containing a value, and [`Err(E)`], representing error
//! and containing an error value.
//!
//! Functions return [`Result`] whenever errors are expected and recoverable. In
//! the `std` crate, [`Result`] is most prominently used for `Identity`
//! interactions and cryptographic operations.
//!
//! A simple function returning [`Result`] might be defined and used like so:
//!
//! ```
//! enum Version {
//! Version1,
//! Version2,
//! }
//!
//! enum VersionError {
//! InvalidNumber,
//! }
//!
//! fn parse_version(version_number: u8) -> Result<Version, VersionError> {
//! match version_number {
//! 1 => Ok(Version::Version1),
//! 2 => Ok(Version::Version2),
//! _ => Err(VersionError::InvalidNumber),
//! }
//! }
//! ```
//!
//! # Method overview
//!
//! In addition to working with pattern matching, [`Result`] provides a variety
//! of methods.
//!
//! ## Querying the variant
//!
//! The [`is_ok`] and [`is_err`] methods return [`true`] if the [`Result`] is
//! [`Ok`] or [`Err`], respectively.
//!
//! [`is_ok`]: Result::is_ok
//! [`is_err`]: Result::is_err
//!
//! ## Extracting the contained value
//!
//! These methods exctract the contained value in a [`Result<T,E>`] when it is
//! the [`Ok`] variant. If the [`Result`] is [`Err`]:
//!
//! * [`unwrap`] reverts
//! * [`unwrap_or`] returns the default provided value
//!
//! [`unwrap`] Result::unwrap
//! [`unwrap_or`] Result::unwrap_or
library result;

use ::revert::revert;
Expand All @@ -25,6 +74,21 @@ impl<T, E> Result<T, E> {
// Querying the contained values
/////////////////////////////////////////////////////////////////////////
/// Returns `true` if the result is [`Ok`].
///
/// # Examples
///
/// ```
/// enum Error {
/// NotFound,
/// Invalid,
/// }
///
/// let x: Result<u64, Error> = Result::Ok(42);
/// assert(x.is_ok());
///
/// let y: Result<u64, Error> = Result::Err(Error::NotFound));
/// assert(!x.is_ok());
/// ```
pub fn is_ok(self) -> bool {
match self {
Result::Ok(_) => true,
Expand All @@ -33,6 +97,21 @@ impl<T, E> Result<T, E> {
}

/// Returns `true` if the result is [`Err`].
///
/// # Examples
///
/// ```
/// enum Error {
/// NotFound,
/// Invalid,
/// }
///
/// let x: Result<u64, Error> = Result::Ok(42);
/// assert(!x.is_err());
///
/// let y: Result<u64, Error> = Result::Err(Error::NotFound));
/// assert(x.is_err());
/// ```
pub fn is_err(self) -> bool {
match self {
Result::Ok(_) => false,
Expand All @@ -45,10 +124,57 @@ impl<T, E> Result<T, E> {
/// Because this function may revert, its use is generally discouraged.
/// Instead, prefer to use pattern matching and handle the [`Err`]
/// case explicitly.
///
/// # Reverts
///
/// Reverts if the self value is [`Err`].
///
/// # Examples
///
/// ```
/// enum Error {
/// NotFound,
/// Invalid,
/// }
///
/// let x: Result<u64, Error> = Result::Ok(42);
/// assert(x.unwrap() == 42);
///
/// let y: Result<u64, Error> = Result::Err(Error::NotFound));
/// assert(x.unwrap() == 42); // reverts
/// ```
pub fn unwrap(self) -> T {
match self {
Result::Ok(inner_value) => inner_value,
_ => revert(0),
}
}

/// Returns the contained [`Ok`] value or a provided default.
///
/// # Examples
///
/// ```
/// enum Error {
/// NotFound,
/// Invalid,
/// }
///
/// let x: Result<u64, Error> = Result::Ok(42);
/// assert(x.unwrap_or(69) == 42);
///
/// let y: Result<u64, Error> = Result::Err(Error::NotFound));
/// assert(x.unwrap_or(69) == 69);
/// ```
pub fn unwrap_or(self, default: T) -> T {
match self {
Result::Ok(inner_value) => inner_value,
Result::Err(_) => default,
}
}

// TODO: Implement the following transforms when Option and Result can
// import one another:
// - `ok(self) -> Option<T>`
// - `err(self) -> Option<E>`
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
library data_structures;

use core::ops::*;
use std::hash::sha256;

/////////////////////////////////////////////////////////////////////////////
// Data Structures Used in in the Tests
/////////////////////////////////////////////////////////////////////////////
pub struct MyStruct {
x: u64,
y: u64,
}

pub enum MyEnum {
X: u64,
Y: u64,
}

impl Eq for MyStruct {
fn eq(self, other: Self) -> bool {
self.x == other.x && self.y == other.y
}
}

impl Eq for MyEnum {
fn eq(self, other: Self) -> bool {
match (self, other) {
(MyEnum::X(val1), MyEnum::X(val2)) => val1 == val2,
(MyEnum::Y(val1), MyEnum::Y(val2)) => val1 == val2,
_ => false,
}
}
}

impl Eq for (u64, u64) {
fn eq(self, other: Self) -> bool {
self.0 == other.0 && self.1 == other.1
}
}

impl Eq for [u64; 3] {
fn eq(self, other: Self) -> bool {
self[0] == other[0] && self[1] == other[1] && self[2] == other[2]
}
}

impl Eq for str[4] {
fn eq(self, other: Self) -> bool {
sha256(self) == sha256(other)
}
}
Original file line number Diff line number Diff line change
@@ -1,37 +1,21 @@
script;

use std::result::Result;
use std::revert::revert;
dep data_structures;
dep tests;

use tests::*;

fn main() -> bool {
test_ok();
test_err();
test_unwrap_ok();
test_bool();
test_u8();
test_u16();
test_u32();
test_u64();
test_struct();
test_enum();
test_tuple();
test_array();
test_string();

true
}

fn test_ok() {
let r = Result::Ok::<u64, ()>(42u64);

if (!r.is_ok() || r.is_err()) {
revert(0);
}
}

fn test_err() {
let r = Result::Err::<(), ()>(());

if (r.is_ok() || !r.is_err()) {
revert(0);
}
}

fn test_unwrap_ok() {
let r = Result::Ok::<u64, ()>(42);

let u = r.unwrap();
if (u != 42) {
revert(0);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
library tests;

dep data_structures;

use data_structures::*;
use core::ops::*;

/////////////////////////////////////////////////////////////////////////////
// Generic Tests
/////////////////////////////////////////////////////////////////////////////
fn test_is_ok<T>(val: T) {
assert(Result::Ok::<T, T>(val).is_ok());
assert(!Result::Err::<T, T>(val).is_ok());
}

fn test_is_err<T>(val: T) {
assert(!Result::Ok::<T, T>(val).is_err());
assert(Result::Err::<T, T>(val).is_err());
}

fn test_unwrap<T>(val: T)
where
T: Eq
{
assert(Result::Ok::<T, T>(val).unwrap() == val);
}

// TODO: Combine following two functions when the following issue is resolved:
// https://github.com/FuelLabs/sway/issues/3623
fn test_unwrap_or_ok<T>(val: T, default: T)
where
T: Eq
{
assert(Result::Ok::<T, T>(val).unwrap_or(default) == val);
}
fn test_unwrap_or_err<T>(val: T, default: T)
where
T: Eq
{
assert(Result::Err::<T, T>(val).unwrap_or(default) == default);
}

/////////////////////////////////////////////////////////////////////////////
// Tests for Various Types
/////////////////////////////////////////////////////////////////////////////
pub fn test_bool() {
test_is_ok(true);
test_is_err(true);
test_unwrap(true);
test_unwrap_or_ok(true, false);
test_unwrap_or_err(true, false);
}

pub fn test_u8() {
test_is_ok(42_u8);
test_is_err(42_u8);
test_unwrap(42_u8);
test_unwrap_or_ok(42_u8, 69_u8);
test_unwrap_or_err(42_u8, 69_u8);
}

pub fn test_u16() {
test_is_ok(42_u16);
test_is_err(42_u16);
test_unwrap(42_u16);
test_unwrap_or_ok(42_u64, 69_u64);
test_unwrap_or_err(42_u16, 69_u16);
}

pub fn test_u32() {
test_is_ok(42_u32);
test_is_err(42_u32);
test_unwrap(42_u32);
test_unwrap_or_ok(42_u64, 69_u64);
test_unwrap_or_err(42_u32, 69_u32);
}

pub fn test_u64() {
test_is_ok(42_u64);
test_is_err(42_u64);
test_unwrap(42_u64);
test_unwrap_or_ok(42_u64, 69_u64);
test_unwrap_or_err(42_u64, 69_u64);
}

pub fn test_struct() {
let s = MyStruct { x: 42, y: 43 };
test_is_ok(s);
test_is_err(s);
test_unwrap(s);
test_unwrap_or_ok(s, MyStruct { x: 69, y: 69 });
test_unwrap_or_err(s, MyStruct { x: 69, y: 69 });
}

pub fn test_enum() {
let e = MyEnum::Y(42);
test_is_ok(e);
test_is_err(e);
test_unwrap(e);
test_unwrap_or_ok(e, MyEnum::X(69));
test_unwrap_or_err(e, MyEnum::X(69));
}

pub fn test_tuple() {
let t = (42, 43);
test_is_ok(t);
test_is_err(t);
test_unwrap(t);
test_unwrap_or_ok(t, (69, 70));
test_unwrap_or_err(t, (69, 70));
}

pub fn test_array() {
let a = [42, 43, 44];
test_is_ok(a);
test_is_err(a);
test_unwrap(a);
test_unwrap_or_ok(a, [69, 70, 71]);
test_unwrap_or_err(a, [69, 70, 71]);
}

pub fn test_string() {
let s = "fuel";
test_is_ok(s);
test_is_err(s);
test_unwrap(s);
test_unwrap_or_ok(s, "sway");
test_unwrap_or_err(s, "sway");
}

0 comments on commit de27066

Please sign in to comment.