1
Fork 0

std: Stabilize last bits of io::Error

This commit stabilizes a few remaining bits of the `io::Error` type:

* The `Error::new` method is now stable. The last `detail` parameter was removed
  and the second `desc` parameter was generalized to `E: Into<Box<Error>>` to
  allow creating an I/O error from any form of error. Currently there is no form
  of downcasting, but this will be added in time.

* An implementation of `From<&str> for Box<Error>` was added to liballoc to
  allow construction of errors from raw strings.

* The `Error::raw_os_error` method was stabilized as-is.

* Trait impls for `Clone`, `Eq`, and `PartialEq` were removed from `Error` as it
  is not possible to use them with trait objects.

This is a breaking change due to the modification of the `new` method as well as
the removal of the trait implementations for the `Error` type.

[breaking-change]
This commit is contained in:
Alex Crichton 2015-03-31 16:01:03 -07:00
parent 80bf31dd51
commit ac77392f8a
23 changed files with 109 additions and 96 deletions

View file

@ -56,8 +56,10 @@ use core::fmt;
use core::hash::{self, Hash}; use core::hash::{self, Hash};
use core::mem; use core::mem;
use core::ops::{Deref, DerefMut}; use core::ops::{Deref, DerefMut};
use core::ptr::Unique; use core::ptr::{self, Unique};
use core::raw::TraitObject; use core::raw::{TraitObject, Slice};
use heap;
/// A value that represents the heap. This is the default place that the `box` /// A value that represents the heap. This is the default place that the `box`
/// keyword allocates into when no place is supplied. /// keyword allocates into when no place is supplied.
@ -327,3 +329,43 @@ impl<'a, E: Error + 'a> FromError<E> for Box<Error + 'a> {
Box::new(err) Box::new(err)
} }
} }
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, E: Error + Send + 'a> From<E> for Box<Error + Send + 'a> {
fn from(err: E) -> Box<Error + Send + 'a> {
Box::new(err)
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, 'b> From<&'b str> for Box<Error + Send + 'a> {
fn from(err: &'b str) -> Box<Error + Send + 'a> {
#[derive(Debug)]
struct StringError(Box<str>);
impl Error for StringError {
fn description(&self) -> &str { &self.0 }
}
impl fmt::Display for StringError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
// Unfortunately `String` is located in libcollections, so we construct
// a `Box<str>` manually here.
unsafe {
let alloc = if err.len() == 0 {
0 as *mut u8
} else {
let ptr = heap::allocate(err.len(), 1);
if ptr.is_null() { ::oom(); }
ptr as *mut u8
};
ptr::copy(err.as_bytes().as_ptr(), alloc, err.len());
Box::new(StringError(mem::transmute(Slice {
data: alloc,
len: err.len(),
})))
}
}
}

View file

@ -78,6 +78,7 @@
#![feature(unsafe_no_drop_flag, filling_drop)] #![feature(unsafe_no_drop_flag, filling_drop)]
#![feature(core)] #![feature(core)]
#![feature(unique)] #![feature(unique)]
#![feature(convert)]
#![cfg_attr(test, feature(test, alloc, rustc_private))] #![cfg_attr(test, feature(test, alloc, rustc_private))]
#![cfg_attr(all(not(feature = "external_funcs"), not(feature = "external_crate")), #![cfg_attr(all(not(feature = "external_funcs"), not(feature = "external_crate")),
feature(libc))] feature(libc))]

View file

@ -862,8 +862,8 @@ pub mod writer {
} else if 0x100 <= n && n < NUM_TAGS { } else if 0x100 <= n && n < NUM_TAGS {
w.write_all(&[0xf0 | (n >> 8) as u8, n as u8]) w.write_all(&[0xf0 | (n >> 8) as u8, n as u8])
} else { } else {
Err(io::Error::new(io::ErrorKind::Other, "invalid tag", Err(io::Error::new(io::ErrorKind::Other,
Some(n.to_string()))) &format!("invalid tag: {}", n)[..]))
} }
} }
@ -876,7 +876,7 @@ pub mod writer {
4 => w.write_all(&[0x10 | ((n >> 24) as u8), (n >> 16) as u8, 4 => w.write_all(&[0x10 | ((n >> 24) as u8), (n >> 16) as u8,
(n >> 8) as u8, n as u8]), (n >> 8) as u8, n as u8]),
_ => Err(io::Error::new(io::ErrorKind::Other, _ => Err(io::Error::new(io::ErrorKind::Other,
"isize too big", Some(n.to_string()))) &format!("isize too big: {}", n)[..]))
} }
} }
@ -885,8 +885,8 @@ pub mod writer {
if n < 0x4000 { return write_sized_vuint(w, n, 2); } if n < 0x4000 { return write_sized_vuint(w, n, 2); }
if n < 0x200000 { return write_sized_vuint(w, n, 3); } if n < 0x200000 { return write_sized_vuint(w, n, 3); }
if n < 0x10000000 { return write_sized_vuint(w, n, 4); } if n < 0x10000000 { return write_sized_vuint(w, n, 4); }
Err(io::Error::new(io::ErrorKind::Other, "isize too big", Err(io::Error::new(io::ErrorKind::Other,
Some(n.to_string()))) &format!("isize too big: {}", n)[..]))
} }
impl<'a> Encoder<'a> { impl<'a> Encoder<'a> {
@ -1077,8 +1077,8 @@ pub mod writer {
self.wr_tagged_raw_u32(EsSub32 as usize, v) self.wr_tagged_raw_u32(EsSub32 as usize, v)
} else { } else {
Err(io::Error::new(io::ErrorKind::Other, Err(io::Error::new(io::ErrorKind::Other,
"length or variant id too big", &format!("length or variant id too big: {}",
Some(v.to_string()))) v)[..]))
} }
} }

View file

@ -20,9 +20,7 @@ pub fn realpath(original: &Path) -> io::Result<PathBuf> {
let old = old_path::Path::new(original.to_str().unwrap()); let old = old_path::Path::new(original.to_str().unwrap());
match old_realpath(&old) { match old_realpath(&old) {
Ok(p) => Ok(PathBuf::from(p.as_str().unwrap())), Ok(p) => Ok(PathBuf::from(p.as_str().unwrap())),
Err(e) => Err(io::Error::new(io::ErrorKind::Other, Err(e) => Err(io::Error::new(io::ErrorKind::Other, e))
"realpath error",
Some(e.to_string())))
} }
} }

View file

@ -36,7 +36,6 @@
#![feature(collections)] #![feature(collections)]
#![feature(core)] #![feature(core)]
#![feature(old_fs)] #![feature(old_fs)]
#![feature(io)]
#![feature(old_io)] #![feature(old_io)]
#![feature(old_path)] #![feature(old_path)]
#![feature(os)] #![feature(os)]

View file

@ -47,9 +47,10 @@ pub fn get_sdk_root(sdk_name: &str) -> String {
Ok(String::from_utf8(output.stdout).unwrap()) Ok(String::from_utf8(output.stdout).unwrap())
} else { } else {
let error = String::from_utf8(output.stderr); let error = String::from_utf8(output.stderr);
let error = format!("process exit with error: {}",
error.unwrap());
Err(io::Error::new(io::ErrorKind::Other, Err(io::Error::new(io::ErrorKind::Other,
"process exit with error", &error[..]))
error.ok()))
} }
}); });

View file

@ -67,8 +67,7 @@ impl TempDir {
} }
Err(Error::new(ErrorKind::AlreadyExists, Err(Error::new(ErrorKind::AlreadyExists,
"too many temporary directories already exist", "too many temporary directories already exist"))
None))
} }
/// Attempts to make a temporary directory inside of `env::temp_dir()` whose /// Attempts to make a temporary directory inside of `env::temp_dir()` whose

View file

@ -35,7 +35,6 @@
#![feature(unsafe_destructor)] #![feature(unsafe_destructor)]
#![feature(staged_api)] #![feature(staged_api)]
#![feature(exit_status)] #![feature(exit_status)]
#![feature(io)]
#![feature(set_stdio)] #![feature(set_stdio)]
#![feature(unicode)] #![feature(unicode)]
#![feature(convert)] #![feature(convert)]

View file

@ -699,8 +699,8 @@ fn print_flowgraph<W: Write>(variants: Vec<borrowck_dot::Variant>,
fn expand_err_details(r: io::Result<()>) -> io::Result<()> { fn expand_err_details(r: io::Result<()>) -> io::Result<()> {
r.map_err(|ioerr| { r.map_err(|ioerr| {
io::Error::new(io::ErrorKind::Other, "graphviz::render failed", io::Error::new(io::ErrorKind::Other,
Some(ioerr.to_string())) &format!("graphviz::render failed: {}", ioerr)[..])
}) })
} }
} }

View file

@ -301,7 +301,7 @@ impl fmt::Display for NulError {
impl FromError<NulError> for io::Error { impl FromError<NulError> for io::Error {
fn from_error(_: NulError) -> io::Error { fn from_error(_: NulError) -> io::Error {
io::Error::new(io::ErrorKind::InvalidInput, io::Error::new(io::ErrorKind::InvalidInput,
"data provided contains a nul byte", None) "data provided contains a nul byte")
} }
} }

View file

@ -575,8 +575,7 @@ pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
let to = to.as_ref(); let to = to.as_ref();
if !from.is_file() { if !from.is_file() {
return Err(Error::new(ErrorKind::InvalidInput, return Err(Error::new(ErrorKind::InvalidInput,
"the source path is not an existing file", "the source path is not an existing file"))
None))
} }
let mut reader = try!(File::open(from)); let mut reader = try!(File::open(from));

View file

@ -73,8 +73,7 @@ impl TempDir {
} }
Err(Error::new(ErrorKind::AlreadyExists, Err(Error::new(ErrorKind::AlreadyExists,
"too many temporary directories already exist", "too many temporary directories already exist"))
None))
} }
/// Attempts to make a temporary directory inside of `env::temp_dir()` whose /// Attempts to make a temporary directory inside of `env::temp_dir()` whose

View file

@ -165,7 +165,7 @@ impl<W: Write> BufWriter<W> {
match self.inner.as_mut().unwrap().write(&self.buf[written..]) { match self.inner.as_mut().unwrap().write(&self.buf[written..]) {
Ok(0) => { Ok(0) => {
ret = Err(Error::new(ErrorKind::WriteZero, ret = Err(Error::new(ErrorKind::WriteZero,
"failed to write the buffered data", None)); "failed to write the buffered data"));
break; break;
} }
Ok(n) => written += n, Ok(n) => written += n,

View file

@ -75,8 +75,7 @@ macro_rules! seek {
if pos < 0 { if pos < 0 {
Err(Error::new(ErrorKind::InvalidInput, Err(Error::new(ErrorKind::InvalidInput,
"invalid seek to a negative position", "invalid seek to a negative position"))
None))
} else { } else {
self.pos = pos as u64; self.pos = pos as u64;
Ok(self.pos) Ok(self.pos)

View file

@ -9,12 +9,12 @@
// except according to those terms. // except according to those terms.
use boxed::Box; use boxed::Box;
use clone::Clone; use convert::Into;
use error; use error;
use fmt; use fmt;
use marker::Send;
use option::Option::{self, Some, None}; use option::Option::{self, Some, None};
use result; use result;
use string::String;
use sys; use sys;
/// A type for results generated by I/O related functions where the `Err` type /// A type for results generated by I/O related functions where the `Err` type
@ -31,23 +31,22 @@ pub type Result<T> = result::Result<T, Error>;
/// Errors mostly originate from the underlying OS, but custom instances of /// Errors mostly originate from the underlying OS, but custom instances of
/// `Error` can be created with crafted error messages and a particular value of /// `Error` can be created with crafted error messages and a particular value of
/// `ErrorKind`. /// `ErrorKind`.
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(Debug)]
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub struct Error { pub struct Error {
repr: Repr, repr: Repr,
} }
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(Debug)]
enum Repr { enum Repr {
Os(i32), Os(i32),
Custom(Box<Custom>), Custom(Box<Custom>),
} }
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(Debug)]
struct Custom { struct Custom {
kind: ErrorKind, kind: ErrorKind,
desc: &'static str, error: Box<error::Error+Send>,
detail: Option<String>
} }
/// A list specifying general categories of I/O error. /// A list specifying general categories of I/O error.
@ -125,18 +124,34 @@ pub enum ErrorKind {
} }
impl Error { impl Error {
/// Creates a new custom error from a specified kind/description/detail. /// Creates a new I/O error from a known kind of error as well as an
#[unstable(feature = "io", reason = "the exact makeup of an Error may /// arbitrary error payload.
change to include `Box<Error>` for \ ///
example")] /// This function is used to generically create I/O errors which do not
pub fn new(kind: ErrorKind, /// originate from the OS itself. The `error` argument is an arbitrary
description: &'static str, /// payload which will be contained in this `Error`. Accessors as well as
detail: Option<String>) -> Error { /// downcasting will soon be added to this type as well to access the custom
/// information.
///
/// # Examples
///
/// ```
/// use std::io::{Error, ErrorKind};
///
/// // errors can be created from strings
/// let custom_error = Error::new(ErrorKind::Other, "oh no!");
///
/// // errors can also be created from other errors
/// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn new<E>(kind: ErrorKind, error: E) -> Error
where E: Into<Box<error::Error+Send>>
{
Error { Error {
repr: Repr::Custom(Box::new(Custom { repr: Repr::Custom(Box::new(Custom {
kind: kind, kind: kind,
desc: description, error: error.into(),
detail: detail,
})) }))
} }
} }
@ -162,8 +177,7 @@ impl Error {
/// ///
/// If this `Error` was constructed via `last_os_error` then this function /// If this `Error` was constructed via `last_os_error` then this function
/// will return `Some`, otherwise it will return `None`. /// will return `Some`, otherwise it will return `None`.
#[unstable(feature = "io", reason = "function was just added and the return \ #[stable(feature = "rust1", since = "1.0.0")]
type may become an abstract OS error")]
pub fn raw_os_error(&self) -> Option<i32> { pub fn raw_os_error(&self) -> Option<i32> {
match self.repr { match self.repr {
Repr::Os(i) => Some(i), Repr::Os(i) => Some(i),
@ -179,27 +193,6 @@ impl Error {
Repr::Custom(ref c) => c.kind, Repr::Custom(ref c) => c.kind,
} }
} }
/// Returns a short description for this error message
#[unstable(feature = "io")]
#[deprecated(since = "1.0.0", reason = "use the Error trait's description \
method instead")]
pub fn description(&self) -> &str {
match self.repr {
Repr::Os(..) => "os error",
Repr::Custom(ref c) => c.desc,
}
}
/// Returns a detailed error message for this error (if one is available)
#[unstable(feature = "io")]
#[deprecated(since = "1.0.0", reason = "use the to_string() method instead")]
pub fn detail(&self) -> Option<String> {
match self.repr {
Repr::Os(code) => Some(sys::os::error_string(code)),
Repr::Custom(ref s) => s.detail.clone(),
}
}
} }
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
@ -210,21 +203,7 @@ impl fmt::Display for Error {
let detail = sys::os::error_string(code); let detail = sys::os::error_string(code);
write!(fmt, "{} (os error {})", detail, code) write!(fmt, "{} (os error {})", detail, code)
} }
Repr::Custom(ref c) => { Repr::Custom(ref c) => c.error.fmt(fmt),
match **c {
Custom {
kind: ErrorKind::Other,
desc: "unknown error",
detail: Some(ref detail)
} => {
write!(fmt, "{}", detail)
}
Custom { detail: None, desc, .. } =>
write!(fmt, "{}", desc),
Custom { detail: Some(ref detail), desc, .. } =>
write!(fmt, "{} ({})", desc, detail)
}
}
} }
} }
} }
@ -234,7 +213,7 @@ impl error::Error for Error {
fn description(&self) -> &str { fn description(&self) -> &str {
match self.repr { match self.repr {
Repr::Os(..) => "os error", Repr::Os(..) => "os error",
Repr::Custom(ref c) => c.desc, Repr::Custom(ref c) => c.error.description(),
} }
} }
} }

View file

@ -180,7 +180,7 @@ impl<'a> Write for &'a mut [u8] {
if try!(self.write(data)) == data.len() { if try!(self.write(data)) == data.len() {
Ok(()) Ok(())
} else { } else {
Err(Error::new(ErrorKind::WriteZero, "failed to write whole buffer", None)) Err(Error::new(ErrorKind::WriteZero, "failed to write whole buffer"))
} }
} }

View file

@ -83,7 +83,7 @@ fn append_to_string<F>(buf: &mut String, f: F) -> Result<usize>
if str::from_utf8(&g.s[g.len..]).is_err() { if str::from_utf8(&g.s[g.len..]).is_err() {
ret.and_then(|_| { ret.and_then(|_| {
Err(Error::new(ErrorKind::InvalidInput, Err(Error::new(ErrorKind::InvalidInput,
"stream did not contain valid UTF-8", None)) "stream did not contain valid UTF-8"))
}) })
} else { } else {
g.len = g.s.len(); g.len = g.s.len();
@ -359,8 +359,7 @@ pub trait Write {
while buf.len() > 0 { while buf.len() > 0 {
match self.write(buf) { match self.write(buf) {
Ok(0) => return Err(Error::new(ErrorKind::WriteZero, Ok(0) => return Err(Error::new(ErrorKind::WriteZero,
"failed to write whole buffer", "failed to write whole buffer")),
None)),
Ok(n) => buf = &buf[n..], Ok(n) => buf = &buf[n..],
Err(ref e) if e.kind() == ErrorKind::Interrupted => {} Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
Err(e) => return Err(e), Err(e) => return Err(e),
@ -780,7 +779,7 @@ pub struct Chars<R> {
/// An enumeration of possible errors that can be generated from the `Chars` /// An enumeration of possible errors that can be generated from the `Chars`
/// adapter. /// adapter.
#[derive(PartialEq, Clone, Debug)] #[derive(Debug)]
#[unstable(feature = "io", reason = "awaiting stability of Read::chars")] #[unstable(feature = "io", reason = "awaiting stability of Read::chars")]
pub enum CharsError { pub enum CharsError {
/// Variant representing that the underlying stream was read successfully /// Variant representing that the underlying stream was read successfully

View file

@ -435,7 +435,7 @@ impl ToSocketAddrs for str {
match $e { match $e {
Some(r) => r, Some(r) => r,
None => return Err(io::Error::new(io::ErrorKind::InvalidInput, None => return Err(io::Error::new(io::ErrorKind::InvalidInput,
$msg, None)), $msg)),
} }
) )
} }

View file

@ -72,7 +72,7 @@ fn each_addr<A: ToSocketAddrs, F, T>(addr: A, mut f: F) -> io::Result<T>
} }
Err(last_err.unwrap_or_else(|| { Err(last_err.unwrap_or_else(|| {
Error::new(ErrorKind::InvalidInput, Error::new(ErrorKind::InvalidInput,
"could not resolve to any addresses", None) "could not resolve to any addresses")
})) }))
} }

View file

@ -76,7 +76,7 @@ impl UdpSocket {
match try!(addr.to_socket_addrs()).next() { match try!(addr.to_socket_addrs()).next() {
Some(addr) => self.0.send_to(buf, &addr), Some(addr) => self.0.send_to(buf, &addr),
None => Err(Error::new(ErrorKind::InvalidInput, None => Err(Error::new(ErrorKind::InvalidInput,
"no addresses to send data to", None)), "no addresses to send data to")),
} }
} }

View file

@ -461,7 +461,6 @@ impl Child {
return Err(Error::new( return Err(Error::new(
ErrorKind::InvalidInput, ErrorKind::InvalidInput,
"invalid argument: can't kill an exited process", "invalid argument: can't kill an exited process",
None
)) ))
} }

View file

@ -75,7 +75,7 @@ fn sockaddr_to_addr(storage: &libc::sockaddr_storage,
}))) })))
} }
_ => { _ => {
Err(Error::new(ErrorKind::InvalidInput, "invalid argument", None)) Err(Error::new(ErrorKind::InvalidInput, "invalid argument"))
} }
} }
} }
@ -158,8 +158,7 @@ pub fn lookup_addr(addr: &IpAddr) -> io::Result<String> {
match from_utf8(data.to_bytes()) { match from_utf8(data.to_bytes()) {
Ok(name) => Ok(name.to_string()), Ok(name) => Ok(name.to_string()),
Err(_) => Err(io::Error::new(io::ErrorKind::Other, Err(_) => Err(io::Error::new(io::ErrorKind::Other,
"failed to lookup address information", "failed to lookup address information"))
Some("invalid host name".to_string())))
} }
} }

View file

@ -35,7 +35,8 @@ pub fn cvt_gai(err: c_int) -> io::Result<()> {
.to_string() .to_string()
}; };
Err(io::Error::new(io::ErrorKind::Other, Err(io::Error::new(io::ErrorKind::Other,
"failed to lookup address information", Some(detail))) &format!("failed to lookup address information: {}",
detail)[..]))
} }
impl Socket { impl Socket {