core: Implement unwrap()/unwrap_err() on Result

Now that std::fmt is in libcore, it's possible to implement this as an inherit
method rather than through extension traits.

This commit also tweaks the failure interface of libcore to libstd to what it
should be, one method taking &fmt::Arguments
This commit is contained in:
Alex Crichton 2014-05-10 13:46:05 -07:00
parent cf0619383d
commit c255568652
6 changed files with 66 additions and 348 deletions

View File

@ -9,21 +9,28 @@
// except according to those terms.
//! Failure support for libcore
//!
//! The core library cannot define failure, but it does *declare* failure. This
//! means that the functions inside of libcore are allowed to fail, but to be
//! useful an upstream crate must define failure for libcore to use. The current
//! interface for failure is:
//!
//! fn begin_unwind(fmt: &fmt::Arguments, file: &str, line: uint) -> !;
//!
//! This definition allows for failing with any general message, but it does not
//! allow for failing with a `~Any` value. The reason for this is that libcore
//! is not allowed to allocate.
//!
//! This module contains a few other failure functions, but these are just the
//! necessary lang items for the compiler. All failure is funneled through this
//! one function. Currently, the actual symbol is declared in the standard
//! library, but the location of this may change over time.
#![allow(dead_code, missing_doc)]
#[cfg(not(test))]
use str::raw::c_str_to_static_slice;
// FIXME: Once std::fmt is in libcore, all of these functions should delegate
// to a common failure function with this signature:
//
// extern {
// fn rust_unwind(f: &fmt::Arguments, file: &str, line: uint) -> !;
// }
//
// Each of these functions can create a temporary fmt::Arguments
// structure to pass to this function.
use fmt;
#[cold] #[inline(never)] // this is the slow path, always
#[lang="fail_"]
@ -32,7 +39,11 @@ fn fail_(expr: *u8, file: *u8, line: uint) -> ! {
unsafe {
let expr = c_str_to_static_slice(expr as *i8);
let file = c_str_to_static_slice(file as *i8);
begin_unwind(expr, file, line)
format_args!(|args| -> () {
begin_unwind(args, file, line);
}, "{}", expr);
loop {}
}
}
@ -40,16 +51,19 @@ fn fail_(expr: *u8, file: *u8, line: uint) -> ! {
#[lang="fail_bounds_check"]
#[cfg(not(test))]
fn fail_bounds_check(file: *u8, line: uint, index: uint, len: uint) -> ! {
#[allow(ctypes)]
extern { fn rust_fail_bounds_check(file: *u8, line: uint,
index: uint, len: uint,) -> !; }
unsafe { rust_fail_bounds_check(file, line, index, len) }
let file = unsafe { c_str_to_static_slice(file as *i8) };
format_args!(|args| -> () {
begin_unwind(args, file, line);
}, "index out of bounds: the len is {} but the index is {}", len, index);
loop {}
}
#[cold]
pub fn begin_unwind(msg: &str, file: &'static str, line: uint) -> ! {
pub fn begin_unwind(fmt: &fmt::Arguments, file: &'static str, line: uint) -> ! {
// FIXME: this should be a proper lang item, it should not just be some
// undefined symbol sitting in the middle of nowhere.
#[allow(ctypes)]
extern { fn rust_begin_unwind(msg: &str, file: &'static str,
extern { fn rust_begin_unwind(fmt: &fmt::Arguments, file: &'static str,
line: uint) -> !; }
unsafe { rust_begin_unwind(msg, file, line) }
unsafe { rust_begin_unwind(fmt, file, line) }
}

View File

@ -268,6 +268,7 @@
use clone::Clone;
use cmp::Eq;
use fmt::Show;
use iter::{Iterator, FromIterator};
use option::{None, Option, Some};
@ -515,6 +516,34 @@ impl<T, E> Result<T, E> {
}
}
impl<T, E: Show> Result<T, E> {
/// Unwraps a result, yielding the content of an `Ok`.
///
/// Fails if the value is an `Err`.
#[inline]
pub fn unwrap(self) -> T {
match self {
Ok(t) => t,
Err(e) =>
fail!("called `Result::unwrap()` on an `Err` value: {}", e)
}
}
}
impl<T: Show, E> Result<T, E> {
/// Unwraps a result, yielding the content of an `Err`.
///
/// Fails if the value is an `Ok`.
#[inline]
pub fn unwrap_err(self) -> E {
match self {
Ok(t) =>
fail!("called `Result::unwrap_err()` on an `Ok` value: {}", t),
Err(e) => e
}
}
}
/////////////////////////////////////////////////////////////////////////////
// Free functions
/////////////////////////////////////////////////////////////////////////////

View File

@ -153,6 +153,7 @@ pub use core::mem;
pub use core::ptr;
pub use core::raw;
pub use core::tuple;
pub use core::result;
// Run tests with libgreen instead of libnative.
//
@ -218,7 +219,6 @@ pub mod hash;
/* Common data structures */
pub mod result;
pub mod option;
/* Tasks and communication */

View File

@ -64,13 +64,12 @@ pub use iter::{Iterator, DoubleEndedIterator, RandomAccessIterator, CloneableIte
pub use iter::{OrdIterator, MutableDoubleEndedIterator, ExactSize};
pub use num::{Num, NumCast, CheckedAdd, CheckedSub, CheckedMul};
pub use num::{Signed, Unsigned};
pub use num::{Primitive, Int, Float, ToPrimitive, FromPrimitive};
pub use num::{Primitive, Int, Float, FloatMath, ToPrimitive, FromPrimitive};
pub use option::Expect;
pub use owned::Box;
pub use path::{GenericPath, Path, PosixPath, WindowsPath};
pub use ptr::RawPtr;
pub use io::{Buffer, Writer, Reader, Seek};
pub use result::{ResultUnwrap, ResultUnwrapErr};
pub use str::{Str, StrVector, StrSlice, OwnedStr, IntoMaybeOwned};
pub use str::{StrAllocating};
pub use to_str::{ToStr, IntoStr};

View File

@ -1,312 +0,0 @@
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Error handling with the `Result` type
//!
//! `Result<T>` is the type used for returning and propagating
//! 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.
//!
//! ~~~
//! enum Result<T, E> {
//! Ok(T),
//! Err(E)
//! }
//! ~~~
//!
//! Functions return `Result` whenever errors are expected and
//! recoverable. In the `std` crate `Result` is most prominently used
//! for [I/O](../io/index.html).
//!
//! A simple function returning `Result` might be
//! defined and used like so:
//!
//! ~~~
//! #[deriving(Show)]
//! enum Version { Version1, Version2 }
//!
//! fn parse_version(header: &[u8]) -> Result<Version, &'static str> {
//! if header.len() < 1 {
//! return Err("invalid header length");
//! }
//! match header[0] {
//! 1 => Ok(Version1),
//! 2 => Ok(Version2),
//! _ => Err("invalid version")
//! }
//! }
//!
//! let version = parse_version(&[1, 2, 3, 4]);
//! match version {
//! Ok(v) => {
//! println!("working with version: {}", v);
//! }
//! Err(e) => {
//! println!("error parsing header: {}", e);
//! }
//! }
//! ~~~
//!
//! Pattern matching on `Result`s is clear and straightforward for
//! simple cases, but `Result` comes with some convenience methods
//! that make working it more succinct.
//!
//! ~~~
//! let good_result: Result<int, int> = Ok(10);
//! let bad_result: Result<int, int> = Err(10);
//!
//! // The `is_ok` and `is_err` methods do what they say.
//! assert!(good_result.is_ok() && !good_result.is_err());
//! assert!(bad_result.is_err() && !bad_result.is_ok());
//!
//! // `map` consumes the `Result` and produces another.
//! let good_result: Result<int, int> = good_result.map(|i| i + 1);
//! let bad_result: Result<int, int> = bad_result.map(|i| i - 1);
//!
//! // Use `and_then` to continue the computation.
//! let good_result: Result<bool, int> = good_result.and_then(|i| Ok(i == 11));
//!
//! // Use `or_else` to handle the error.
//! let bad_result: Result<int, int> = bad_result.or_else(|i| Ok(11));
//!
//! // Consume the result and return the contents with `unwrap`.
//! let final_awesome_result = good_result.ok().unwrap();
//! ~~~
//!
//! # Results must be used
//!
//! A common problem with using return values to indicate errors is
//! that it is easy to ignore the return value, thus failing to handle
//! the error. Result is annotated with the #[must_use] attribute,
//! which will cause the compiler to issue a warning when a Result
//! value is ignored. This makes `Result` especially useful with
//! functions that may encounter errors but don't otherwise return a
//! useful value.
//!
//! Consider the `write_line` method defined for I/O types
//! by the [`Writer`](../io/trait.Writer.html) trait:
//!
//! ~~~
//! use std::io::IoError;
//!
//! trait Writer {
//! fn write_line(&mut self, s: &str) -> Result<(), IoError>;
//! }
//! ~~~
//!
//! *Note: The actual definition of `Writer` uses `IoResult`, which
//! is just a synonym for `Result<T, IoError>`.*
//!
//! This method doesn`t produce a value, but the write may
//! fail. It's crucial to handle the error case, and *not* write
//! something like this:
//!
//! ~~~ignore
//! use std::io::{File, Open, Write};
//!
//! let mut file = File::open_mode(&Path::new("valuable_data.txt"), Open, Write);
//! // If `write_line` errors, then we'll never know, because the return
//! // value is ignored.
//! file.write_line("important message");
//! drop(file);
//! ~~~
//!
//! If you *do* write that in Rust, the compiler will by give you a
//! warning (by default, controlled by the `unused_must_use` lint).
//!
//! You might instead, if you don't want to handle the error, simply
//! fail, by converting to an `Option` with `ok`, then asserting
//! success with `expect`. This will fail if the write fails, proving
//! a marginally useful message indicating why:
//!
//! ~~~no_run
//! use std::io::{File, Open, Write};
//!
//! let mut file = File::open_mode(&Path::new("valuable_data.txt"), Open, Write);
//! file.write_line("important message").ok().expect("failed to write message");
//! drop(file);
//! ~~~
//!
//! You might also simply assert success:
//!
//! ~~~no_run
//! # use std::io::{File, Open, Write};
//!
//! # let mut file = File::open_mode(&Path::new("valuable_data.txt"), Open, Write);
//! assert!(file.write_line("important message").is_ok());
//! # drop(file);
//! ~~~
//!
//! Or propagate the error up the call stack with `try!`:
//!
//! ~~~
//! # use std::io::{File, Open, Write, IoError};
//! fn write_message() -> Result<(), IoError> {
//! let mut file = File::open_mode(&Path::new("valuable_data.txt"), Open, Write);
//! try!(file.write_line("important message"));
//! drop(file);
//! return Ok(());
//! }
//! ~~~
//!
//! # The `try!` macro
//!
//! When writing code that calls many functions that return the
//! `Result` type, the error handling can be tedious. The `try!`
//! macro hides some of the boilerplate of propagating errors up the
//! call stack.
//!
//! It replaces this:
//!
//! ~~~
//! use std::io::{File, Open, Write, IoError};
//!
//! struct Info { name: ~str, age: int, rating: int }
//!
//! fn write_info(info: &Info) -> Result<(), IoError> {
//! let mut file = File::open_mode(&Path::new("my_best_friends.txt"), Open, Write);
//! // Early return on error
//! match file.write_line(format!("name: {}", info.name)) {
//! Ok(_) => (),
//! Err(e) => return Err(e)
//! }
//! match file.write_line(format!("age: {}", info.age)) {
//! Ok(_) => (),
//! Err(e) => return Err(e)
//! }
//! return file.write_line(format!("rating: {}", info.rating));
//! }
//! ~~~
//!
//! With this:
//!
//! ~~~
//! use std::io::{File, Open, Write, IoError};
//!
//! struct Info { name: ~str, age: int, rating: int }
//!
//! fn write_info(info: &Info) -> Result<(), IoError> {
//! let mut file = File::open_mode(&Path::new("my_best_friends.txt"), Open, Write);
//! // Early return on error
//! try!(file.write_line(format!("name: {}", info.name)));
//! try!(file.write_line(format!("age: {}", info.age)));
//! try!(file.write_line(format!("rating: {}", info.rating)));
//! return Ok(());
//! }
//! ~~~
//!
//! *It's much nicer!*
//!
//! Wrapping an expression in `try!` will result in the unwrapped
//! success (`Ok`) value, unless the result is `Err`, in which case
//! `Err` is returned early from the enclosing function. Its simple definition
//! makes it clear:
//!
//! ~~~
//! # #![feature(macro_rules)]
//! macro_rules! try(
//! ($e:expr) => (match $e { Ok(e) => e, Err(e) => return Err(e) })
//! )
//! # fn main() { }
//! ~~~
//!
//! `try!` is imported by the prelude, and is available everywhere.
//!
//! # `Result` and `Option`
//!
//! The `Result` and [`Option`](../option/index.html) types are
//! similar and complementary: they are often employed to indicate a
//! lack of a return value; and they are trivially converted between
//! each other, so `Result`s are often handled by first converting to
//! `Option` with the [`ok`](../../core/result/enum.Result.html#method.ok) and
//! [`err`](../../core/result/enum.Result.html#method.ok) methods.
//!
//! Whereas `Option` only indicates the lack of a value, `Result` is
//! specifically for error reporting, and carries with it an error
//! value. Sometimes `Option` is used for indicating errors, but this
//! is only for simple cases and is generally discouraged. Even when
//! there is no useful error value to return, prefer `Result<T, ()>`.
//!
//! Converting to an `Option` with `ok()` to handle an error:
//!
//! ~~~
//! use std::io::Timer;
//! let mut t = Timer::new().ok().expect("failed to create timer!");
//! ~~~
//!
//! # `Result` vs. `fail!`
//!
//! `Result` is for recoverable errors; `fail!` is for unrecoverable
//! errors. Callers should always be able to avoid failure if they
//! take the proper precautions, for example, calling `is_some()`
//! on an `Option` type before calling `unwrap`.
//!
//! The suitability of `fail!` as an error handling mechanism is
//! limited by Rust's lack of any way to "catch" and resume execution
//! from a thrown exception. Therefore using failure for error
//! handling requires encapsulating fallable code in a task. Calling
//! the `fail!` macro, or invoking `fail!` indirectly should be
//! avoided as an error reporting strategy. Failure is only for
//! unrecoverable errors and a failing task is typically the sign of
//! a bug.
//!
//! A module that instead returns `Results` is alerting the caller
//! that failure is possible, and providing precise control over how
//! it is handled.
//!
//! Furthermore, failure may not be recoverable at all, depending on
//! the context. The caller of `fail!` should assume that execution
//! will not resume after failure, that failure is catastrophic.
use fmt::Show;
pub use core::result::{Result, Ok, Err, collect, fold, fold_};
// FIXME: These traits should not exist. Once std::fmt is moved to libcore,
// these can once again become inherent methods on Result.
/// Temporary trait for unwrapping a result
pub trait ResultUnwrap<T, E> {
/// Unwraps a result, yielding the content of an `Ok`.
///
/// Fails if the value is an `Err`.
fn unwrap(self) -> T;
}
/// Temporary trait for unwrapping the error of a result
pub trait ResultUnwrapErr<T, E> {
/// Unwraps a result, yielding the content of an `Err`.
///
/// Fails if the value is an `Ok`.
fn unwrap_err(self) -> E;
}
impl<T, E: Show> ResultUnwrap<T, E> for Result<T, E> {
#[inline]
fn unwrap(self) -> T {
match self {
Ok(t) => t,
Err(e) =>
fail!("called `Result::unwrap()` on an `Err` value: {}", e)
}
}
}
impl<T: Show, E> ResultUnwrapErr<T, E> for Result<T, E> {
#[inline]
fn unwrap_err(self) -> E {
match self {
Ok(t) =>
fail!("called `Result::unwrap_err()` on an `Ok` value: {}", t),
Err(e) => e
}
}
}

View File

@ -295,24 +295,12 @@ pub mod eabi {
}
}
#[cold]
#[no_mangle]
#[cfg(not(test))]
pub extern fn rust_fail_bounds_check(file: *u8, line: uint,
index: uint, len: uint) -> ! {
use str::raw::c_str_to_static_slice;
let msg = format!("index out of bounds: the len is {} but the index is {}",
len as uint, index as uint);
begin_unwind(msg, unsafe { c_str_to_static_slice(file as *i8) }, line)
}
// Entry point of failure from the libcore crate
#[no_mangle]
#[cfg(not(test))]
pub extern fn rust_begin_unwind(msg: &str, file: &'static str, line: uint) -> ! {
use str::StrAllocating;
begin_unwind(msg.to_owned(), file, line)
pub extern fn rust_begin_unwind(msg: &fmt::Arguments,
file: &'static str, line: uint) -> ! {
begin_unwind_fmt(msg, file, line)
}
/// The entry point for unwinding with a formatted message.