Implementing a Robust Result Type in Rust
Rust's Result enum is a cornerstone of its error-handling strategy, providing a safe and expressive way to manage operations that can either succeed or fail. This challenge asks you to implement a custom Result type that mirrors the core functionality of Rust's built-in Result, allowing you to practice fundamental enum, pattern matching, and generic programming concepts in Rust.
Problem Description
Your task is to create a generic enum named MyResult<T, E> that can represent either a successful value of type T or an error value of type E. This type will be used to signify the outcome of operations.
Key Requirements:
-
Enum Definition: Define
MyResult<T, E>with two variants:Ok(T): Represents a successful operation with an associated value of typeT.Err(E): Represents a failed operation with an associated error of typeE.
-
is_ok()Method: Implement a methodis_ok(&self) -> boolthat returnstrueif theMyResultisOk, andfalseotherwise. -
is_err()Method: Implement a methodis_err(&self) -> boolthat returnstrueif theMyResultisErr, andfalseotherwise. -
unwrap()Method: Implement a methodunwrap(self) -> Tthat returns the containedOkvalue. If theMyResultisErr, this method should panic with a descriptive message. -
unwrap_or(self, default: T) -> TMethod: Implement a methodunwrap_or(self, default: T) -> Tthat returns the containedOkvalue. If theMyResultisErr, it should return the provideddefaultvalue. -
map<U, F>(self, f: F) -> MyResult<U, E>Method: Implement a methodmap<U, F>(self, f: F) -> MyResult<U, E>whereF: FnOnce(T) -> U. This method applies the functionfto the containedOkvalue, returning a newMyResultwith the transformed value. If theMyResultisErr, theErrvalue is returned unchanged.
Expected Behavior:
- The
MyResulttype should be capable of holding any type for its success value (T) and any type for its error value (E). - The methods should behave exactly as described above. Panicking should occur only when
unwrap()is called on anErr.
Edge Cases to Consider:
- What happens when
unwrap()is called on anErrvariant? (It should panic). - How does
maphandle anErrvariant? (It should pass theErrthrough unchanged).
Examples
Example 1: Basic Usage
// Assume MyResult is defined and its methods are implemented
let success: MyResult<i32, &str> = MyResult::Ok(10);
let failure: MyResult<i32, &str> = MyResult::Err("Something went wrong");
assert_eq!(success.is_ok(), true);
assert_eq!(success.is_err(), false);
assert_eq!(failure.is_ok(), false);
assert_eq!(failure.is_err(), true);
Example 2: unwrap() and unwrap_or()
let success: MyResult<i32, &str> = MyResult::Ok(10);
let failure: MyResult<i32, &str> = MyResult::Err("Error message");
assert_eq!(success.unwrap(), 10);
assert_eq!(failure.unwrap_or(5), 5);
// The following would panic if uncommented and run:
// failure.unwrap();
Example 3: map()
let success: MyResult<i32, &str> = MyResult::Ok(10);
let failure: MyResult<i32, &str> = MyResult::Err("Error message");
let mapped_success: MyResult<i32, &str> = success.map(|x| x * 2);
assert_eq!(mapped_success, MyResult::Ok(20));
let mapped_failure: MyResult<i32, &str> = failure.map(|x| x * 2);
assert_eq!(mapped_failure, MyResult::Err("Error message"));
let string_success: MyResult<String, i32> = MyResult::Ok("hello".to_string());
let mapped_string_success: MyResult<usize, i32> = string_success.map(|s| s.len());
assert_eq!(mapped_string_success, MyResult::Ok(5));
Constraints
- The generic types
TandEcan be any valid Rust types. - The methods should not have any specific performance overhead beyond what is inherent to Rust's enum and method dispatch.
Notes
- You'll need to use
impl<T, E> MyResult<T, E> { ... }to define methods for your generic enum. - Pay close attention to ownership and borrowing when implementing the methods, especially
unwrap()andmap(). - Consider how you'll handle the panic in
unwrap(). - The
mapmethod requires a closure that takesTand returnsU. The signatureF: FnOnce(T) -> Uis key here.FnOnceis appropriate because the closure is called at most once and may consume its argument. - You might find it useful to derive
DebugandPartialEqforMyResultto make assertions in examples easier to write and verify.