diff options
| -rw-r--r-- | src/libcore/failure.rs | 50 | ||||
| -rw-r--r-- | src/libcore/result.rs | 29 | ||||
| -rw-r--r-- | src/libstd/lib.rs | 2 | ||||
| -rw-r--r-- | src/libstd/prelude.rs | 3 | ||||
| -rw-r--r-- | src/libstd/result.rs | 312 | ||||
| -rw-r--r-- | src/libstd/rt/unwind.rs | 18 |
6 files changed, 66 insertions, 348 deletions
diff --git a/src/libcore/failure.rs b/src/libcore/failure.rs index 8a28f7b1392..c4a2c9a6099 100644 --- a/src/libcore/failure.rs +++ b/src/libcore/failure.rs @@ -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) } } diff --git a/src/libcore/result.rs b/src/libcore/result.rs index cced5028460..81f089ae67d 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -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 ///////////////////////////////////////////////////////////////////////////// diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 87c4ef1046f..119cd9aa2ca 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -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 */ diff --git a/src/libstd/prelude.rs b/src/libstd/prelude.rs index 6cd9e96496f..e39d8d34447 100644 --- a/src/libstd/prelude.rs +++ b/src/libstd/prelude.rs @@ -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}; diff --git a/src/libstd/result.rs b/src/libstd/result.rs deleted file mode 100644 index ecbc164590b..00000000000 --- a/src/libstd/result.rs +++ /dev/null @@ -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 - } - } -} diff --git a/src/libstd/rt/unwind.rs b/src/libstd/rt/unwind.rs index e10e0716f67..c53346f69ee 100644 --- a/src/libstd/rt/unwind.rs +++ b/src/libstd/rt/unwind.rs @@ -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. |
