Skip to content

Commit

Permalink
auto merge of #17753 : aturon/rust/error-interop, r=alexcrichton
Browse files Browse the repository at this point in the history
This PR:

* Adds the error interoperation traits (`Error` and `FromError`) to a new module, `std::error`, as per [RFC 70](https://github.com/rust-lang/rfcs/blob/master/active/0070-error-chaining.md). Note that this module must live in `std` in order to refer to `String`.

    Note that, until multidispatch lands, the `FromError` trait cannot be
usefully implemented outside of the blanket impl given here.

* Incorporates `std::error::FromError` into the `try!` macro.

* Implements `Error` for most existing error enumerations.

Closes #17747
  • Loading branch information
bors committed Oct 31, 2014
2 parents 5e83424 + bc1bc9d commit f964590
Show file tree
Hide file tree
Showing 11 changed files with 238 additions and 6 deletions.
3 changes: 3 additions & 0 deletions src/libcore/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ pub trait AnyRefExt<'a> {
fn downcast_ref<T: 'static>(self) -> Option<&'a T>;
}

// Note: this code is copied to std::error, and should be unified as
// soon as possible.

#[stable]
impl<'a> AnyRefExt<'a> for &'a Any {
#[inline]
Expand Down
4 changes: 2 additions & 2 deletions src/libcore/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ pub use ops::{Shl, Shr};
pub use ops::{Index, IndexMut};
pub use ops::{Slice, SliceMut};
pub use ops::{Fn, FnMut, FnOnce};
pub use option::{Option, Some, None};
pub use result::{Result, Ok, Err};

// Reexported functions
pub use iter::range;
Expand All @@ -57,7 +55,9 @@ pub use iter::{OrdIterator, MutableDoubleEndedIterator, ExactSize};
pub use num::{Num, NumCast, CheckedAdd, CheckedSub, CheckedMul};
pub use num::{Signed, Unsigned, Float};
pub use num::{Primitive, Int, ToPrimitive, FromPrimitive};
pub use option::{Option, Some, None};
pub use ptr::RawPtr;
pub use result::{Result, Ok, Err};
pub use str::{Str, StrSlice};
pub use tuple::{Tuple1, Tuple2, Tuple3, Tuple4};
pub use tuple::{Tuple5, Tuple6, Tuple7, Tuple8};
Expand Down
14 changes: 14 additions & 0 deletions src/libserialize/base64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
//! Base64 binary-to-text encoding
use std::fmt;
use std::string;
use std::error;

/// Available encoding character sets
pub enum CharacterSet {
Expand Down Expand Up @@ -178,6 +179,19 @@ impl fmt::Show for FromBase64Error {
}
}

impl error::Error for FromBase64Error {
fn description(&self) -> &str {
match *self {
InvalidBase64Byte(_, _) => "invalid character",
InvalidBase64Length => "invalid length",
}
}

fn detail(&self) -> Option<String> {
Some(self.to_string())
}
}

impl<'a> FromBase64 for &'a str {
/**
* Convert any base64 encoded string (literal, `@`, `&`, or `~`)
Expand Down
15 changes: 15 additions & 0 deletions src/libserialize/hex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
//! Hex binary-to-text encoding
use std::fmt;
use std::string;
use std::error;

/// A trait for converting a value to hexadecimal encoding
pub trait ToHex {
Expand Down Expand Up @@ -77,6 +78,20 @@ impl fmt::Show for FromHexError {
}
}

impl error::Error for FromHexError {
fn description(&self) -> &str {
match *self {
InvalidHexCharacter(_, _) => "invalid character",
InvalidHexLength => "invalid length",
}
}

fn detail(&self) -> Option<String> {
Some(self.to_string())
}
}


impl<'a> FromHex for &'a str {
/**
* Convert any hexadecimal encoded string (literal, `@`, `&`, or `~`)
Expand Down
5 changes: 5 additions & 0 deletions src/libserialize/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,11 @@ fn io_error_to_error(io: io::IoError) -> ParserError {
IoError(io.kind, io.desc)
}

impl std::error::Error for DecoderError {
fn description(&self) -> &str { "decoder error" }
fn detail(&self) -> Option<std::string::String> { Some(self.to_string()) }
}

pub type EncodeResult = io::IoResult<()>;
pub type DecodeResult<T> = Result<T, DecoderError>;

Expand Down
156 changes: 156 additions & 0 deletions src/libstd/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// Copyright 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.

//! Traits for working with Errors.
//!
//! # The `Error` trait
//!
//! `Error` is a trait representing the basic expectations for error values,
//! i.e. values of type `E` in `Result<T, E>`. At a minimum, errors must provide
//! a description, but they may optionally provide additional detail and cause
//! chain information:
//!
//! ```
//! pub trait Error: Send + Any {
//! fn description(&self) -> &str;
//!
//! fn detail(&self) -> Option<String> { None }
//! fn cause(&self) -> Option<&Error> { None }
//! }
//! ```
//!
//! The `cause` method is generally used when errors cross "abstraction
//! boundaries", i.e. when a one module must report an error that is "caused"
//! by an error from a lower-level module. This setup makes it possible for the
//! high-level module to provide its own errors that do not commit to any
//! particular implementation, but also reveal some of its implementation for
//! debugging via `cause` chains.
//!
//! The trait inherits from `Any` to allow *downcasting*: converting from a
//! trait object to a specific concrete type when applicable.
//!
//! # The `FromError` trait
//!
//! `FromError` is a simple trait that expresses conversions between different
//! error types. To provide maximum flexibility, it does not require either of
//! the types to actually implement the `Error` trait, although this will be the
//! common case.
//!
//! The main use of this trait is in the `try!` macro, which uses it to
//! automatically convert a given error to the error specified in a function's
//! return type.
//!
//! For example,
//!
//! ```
//! use std::io::IoError;
//! use std::os::MapError;
//!
//! impl FromError<IoError> for Box<Error> {
//! fn from_error(err: IoError) -> Box<Error> {
//! box err
//! }
//! }
//!
//! impl FromError<MapError> for Box<Error> {
//! fn from_error(err: MapError) -> Box<Error> {
//! box err
//! }
//! }
//!
//! #[allow(unused_variables)]
//! fn open_and_map() -> Box<Error> {
//! let f = try!(io::File::open("foo.txt"));
//! let m = try!(os::MemoryMap::new(0, &[]));
//! // do something interesting here...
//! }
//! ```
use any::{Any, AnyRefExt, AnyMutRefExt};
use mem::{transmute, transmute_copy};
use option::{Option, Some, None};
use raw::TraitObject;
use intrinsics::TypeId;
use kinds::Send;
use string::String;

/// Base functionality for all errors in Rust.
pub trait Error: Send + Any {
/// A short description of the error; usually a static string.
fn description(&self) -> &str;

/// A detailed description of the error, usually including dynamic information.
fn detail(&self) -> Option<String> { None }

/// The lower-level cause of this error, if any.
fn cause(&self) -> Option<&Error> { None }
}

/// A trait for types that can be converted from a given error type `E`.
pub trait FromError<E> {
/// Perform the conversion.
fn from_error(err: E) -> Self;
}

// Any type is convertable from itself
impl<E> FromError<E> for E {
fn from_error(err: E) -> E {
err
}
}

// Note: the definitions below are copied from core::any, and should be unified
// as soon as possible.

impl<'a> AnyRefExt<'a> for &'a Error {
#[inline]
fn is<T: 'static>(self) -> bool {
// Get TypeId of the type this function is instantiated with
let t = TypeId::of::<T>();

// Get TypeId of the type in the trait object
let boxed = self.get_type_id();

// Compare both TypeIds on equality
t == boxed
}

#[inline]
fn downcast_ref<T: 'static>(self) -> Option<&'a T> {
if self.is::<T>() {
unsafe {
// Get the raw representation of the trait object
let to: TraitObject = transmute_copy(&self);

// Extract the data pointer
Some(transmute(to.data))
}
} else {
None
}
}
}

impl<'a> AnyMutRefExt<'a> for &'a mut Error {
#[inline]
fn downcast_mut<T: 'static>(self) -> Option<&'a mut T> {
if self.is::<T>() {
unsafe {
// Get the raw representation of the trait object
let to: TraitObject = transmute_copy(&self);

// Extract the data pointer
Some(transmute(to.data))
}
} else {
None
}
}
}
18 changes: 18 additions & 0 deletions src/libstd/io/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,9 @@ responding to errors that may occur while attempting to read the numbers.

use char::Char;
use collections::Collection;
use clone::Clone;
use default::Default;
use error::{FromError, Error};
use fmt;
use int;
use iter::Iterator;
Expand Down Expand Up @@ -434,6 +436,22 @@ impl fmt::Show for IoError {
}
}

impl Error for IoError {
fn description(&self) -> &str {
self.desc
}

fn detail(&self) -> Option<String> {
self.detail.clone()
}
}

impl FromError<IoError> for Box<Error> {
fn from_error(err: IoError) -> Box<Error> {
box err
}
}

/// A list specifying general categories of I/O error.
#[deriving(PartialEq, Eq, Clone, Show)]
pub enum IoErrorKind {
Expand Down
2 changes: 2 additions & 0 deletions src/libstd/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ pub mod time;

/* Common traits */

pub mod error;
pub mod from_str;
pub mod num;
pub mod to_string;
Expand Down Expand Up @@ -257,6 +258,7 @@ mod std {
pub use hash;

pub use comm; // used for select!()
pub use error; // used for try!()
pub use fmt; // used for any formatting strings
pub use io; // used for println!()
pub use local_data; // used for local_data_key!()
Expand Down
9 changes: 7 additions & 2 deletions src/libstd/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,8 +317,13 @@ macro_rules! local_data_key(
/// error if the value of the expression is `Err`. For more information, see
/// `std::io`.
#[macro_export]
macro_rules! try(
($e:expr) => (match $e { Ok(e) => e, Err(e) => return Err(e) })
macro_rules! try (
($expr:expr) => ({
match $expr {
Ok(val) => val,
Err(err) => return Err(::std::error::FromError::from_error(err))
}
})
)

/// Create a `std::vec::Vec` containing the arguments.
Expand Down
14 changes: 14 additions & 0 deletions src/libstd/os.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@

use clone::Clone;
use collections::{Collection, MutableSeq};
use error::{FromError, Error};
use fmt;
use io::{IoResult, IoError};
use iter::Iterator;
use libc::{c_void, c_int};
use libc;
use boxed::Box;
use ops::Drop;
use option::{Some, None, Option};
use os;
Expand All @@ -49,6 +51,7 @@ use slice::{AsSlice, ImmutableSlice, MutableSlice, ImmutablePartialEqSlice};
use slice::CloneableVector;
use str::{Str, StrSlice, StrAllocating};
use string::String;
use to_string::ToString;
use sync::atomic::{AtomicInt, INIT_ATOMIC_INT, SeqCst};
use vec::Vec;

Expand Down Expand Up @@ -1438,6 +1441,17 @@ impl fmt::Show for MapError {
}
}

impl Error for MapError {
fn description(&self) -> &str { "memory map error" }
fn detail(&self) -> Option<String> { Some(self.to_string()) }
}

impl FromError<MapError> for Box<Error> {
fn from_error(err: MapError) -> Box<Error> {
box err
}
}

#[cfg(unix)]
impl MemoryMap {
/// Create a new mapping with the given `options`, at least `min_len` bytes
Expand Down
4 changes: 2 additions & 2 deletions src/libstd/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,6 @@
#[doc(no_inline)] pub use ops::{Index, IndexMut};
#[doc(no_inline)] pub use ops::{Slice, SliceMut};
#[doc(no_inline)] pub use ops::{Fn, FnMut, FnOnce};
#[doc(no_inline)] pub use option::{Option, Some, None};
#[doc(no_inline)] pub use result::{Result, Ok, Err};

// Reexported functions
#[doc(no_inline)] pub use from_str::from_str;
Expand All @@ -75,8 +73,10 @@
#[doc(no_inline)] pub use num::{Signed, Unsigned, Primitive, Int, Float};
#[doc(no_inline)] pub use num::{FloatMath, ToPrimitive, FromPrimitive};
#[doc(no_inline)] pub use boxed::Box;
#[doc(no_inline)] pub use option::{Option, Some, None};
#[doc(no_inline)] pub use path::{GenericPath, Path, PosixPath, WindowsPath};
#[doc(no_inline)] pub use ptr::{RawPtr, RawMutPtr};
#[doc(no_inline)] pub use result::{Result, Ok, Err};
#[doc(no_inline)] pub use io::{Buffer, Writer, Reader, Seek};
#[doc(no_inline)] pub use str::{Str, StrVector, StrSlice};
#[doc(no_inline)] pub use str::{IntoMaybeOwned, StrAllocating, UnicodeStrSlice};
Expand Down

0 comments on commit f964590

Please sign in to comment.