From 9bae6ec828fdc7f87838ee008cccef90e31b9f84 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 30 Apr 2014 23:06:36 -0700 Subject: core: Inherit possible string functionality This moves as much allocation as possible from teh std::str module into core::str. This includes essentially all non-allocating functionality, mostly iterators and slicing and such. This primarily splits the Str trait into only having the as_slice() method, adding a new StrAllocating trait to std::str which contains the relevant new allocation methods. This is a breaking change if any of the methods of "trait Str" were overriden. The old functionality can be restored by implementing both the Str and StrAllocating traits. [breaking-change] --- src/libstd/rt/args.rs | 1 - 1 file changed, 1 deletion(-) (limited to 'src/libstd/rt') diff --git a/src/libstd/rt/args.rs b/src/libstd/rt/args.rs index 17e6f6b7698..ac1692e6bb3 100644 --- a/src/libstd/rt/args.rs +++ b/src/libstd/rt/args.rs @@ -70,7 +70,6 @@ mod imp { use owned::Box; use unstable::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; use mem; - #[cfg(not(test))] use str::StrSlice; #[cfg(not(test))] use ptr::RawPtr; static mut global_args_ptr: uint = 0; -- cgit 1.4.1-3-g733a5 From e4271cae54c7f4a868c60a1822b91e702e511947 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 1 May 2014 10:47:18 -0700 Subject: core: Add a limited implementation of failure This adds an small of failure to libcore, hamstrung by the fact that std::fmt hasn't been migrated yet. A few asserts were re-worked to not use std::fmt features, but these asserts can go back to their original form once std::fmt has migrated. The current failure implementation is to just have some symbols exposed by std::rt::unwind that are linked against by libcore. This is an explicit circular dependency, unfortunately. This will be officially supported in the future through compiler support with much nicer failure messages. Additionally, there are two depended-upon symbols today, but in the future there will only be one (once std::fmt has migrated). --- src/libcore/char.rs | 4 ++-- src/libcore/failure.rs | 54 ++++++++++++++++++++++++++++++++++++++++++ src/libcore/lib.rs | 8 ++++++- src/libcore/macros.rs | 38 +++++++++++++++++++++++++++++ src/libcore/num/int_macros.rs | 54 +++++++++++++++++++++--------------------- src/libcore/num/uint_macros.rs | 30 +++++++++++------------ src/libstd/rt/mod.rs | 2 +- src/libstd/rt/unwind.rs | 42 +++++++++----------------------- 8 files changed, 155 insertions(+), 77 deletions(-) create mode 100644 src/libcore/failure.rs create mode 100644 src/libcore/macros.rs (limited to 'src/libstd/rt') diff --git a/src/libcore/char.rs b/src/libcore/char.rs index 9c00b6babc1..298a3b38ee5 100644 --- a/src/libcore/char.rs +++ b/src/libcore/char.rs @@ -214,7 +214,7 @@ pub fn is_digit_radix(c: char, radix: uint) -> bool { #[inline] pub fn to_digit(c: char, radix: uint) -> Option { if radix > 36 { - fail!("to_digit: radix {} is too high (maximum 36)", radix); + fail!("to_digit: radix is too high (maximum 36)"); } let val = match c { '0' .. '9' => c as uint - ('0' as uint), @@ -273,7 +273,7 @@ pub fn to_lowercase(c: char) -> char { #[inline] pub fn from_digit(num: uint, radix: uint) -> Option { if radix > 36 { - fail!("from_digit: radix {} is to high (maximum 36)", num); + fail!("from_digit: radix is to high (maximum 36)"); } if num < radix { unsafe { diff --git a/src/libcore/failure.rs b/src/libcore/failure.rs new file mode 100644 index 00000000000..efa24fa96f8 --- /dev/null +++ b/src/libcore/failure.rs @@ -0,0 +1,54 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Failure support for libcore + +#![allow(dead_code)] + +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. + +#[cold] #[inline(never)] // this is the slow path, always +#[lang="fail_"] +#[cfg(not(test))] +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) + } +} + +#[cold] +#[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) } +} + +#[cold] +pub fn begin_unwind(msg: &str, file: &'static str, line: uint) -> ! { + #[allow(ctypes)] + extern { fn rust_begin_unwind(msg: &str, file: &'static str, + line: uint) -> !; } + unsafe { rust_begin_unwind(msg, file, line) } +} diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index e6fddd5f64e..a0938c83eca 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -21,6 +21,8 @@ #![feature(globs, macro_rules, managed_boxes)] #![deny(missing_doc)] +mod macros; + #[path = "num/float_macros.rs"] mod float_macros; #[path = "num/int_macros.rs"] mod int_macros; #[path = "num/uint_macros.rs"] mod uint_macros; @@ -75,7 +77,11 @@ pub mod slice; pub mod str; pub mod tuple; -// FIXME: this module should not exist +mod failure; + +// FIXME: this module should not exist. Once owned allocations are no longer a +// language type, this module can move outside to the owned allocation +// crate. mod should_not_exist; mod std { diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs new file mode 100644 index 00000000000..50d5cd81ba0 --- /dev/null +++ b/src/libcore/macros.rs @@ -0,0 +1,38 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![macro_escape] + +/// Entry point of failure, for details, see std::macros +#[macro_export] +macro_rules! fail( + () => ( + fail!("explicit failure") + ); + ($msg:expr) => ( + ::failure::begin_unwind($msg, file!(), line!()) + ); +) + +/// Runtime assertion, for details see std::macros +#[macro_export] +macro_rules! assert( + ($cond:expr) => ( + if !$cond { + fail!(concat!("assertion failed: ", stringify!($cond))) + } + ); +) + +/// Runtime assertion, disableable at compile time +#[macro_export] +macro_rules! debug_assert( + ($($arg:tt)*) => (if cfg!(not(ndebug)) { assert!($($arg)*); }) +) diff --git a/src/libcore/num/int_macros.rs b/src/libcore/num/int_macros.rs index fc134df9b94..771d801dcb7 100644 --- a/src/libcore/num/int_macros.rs +++ b/src/libcore/num/int_macros.rs @@ -255,7 +255,7 @@ mod tests { fn test_overflows() { assert!(MAX > 0); assert!(MIN <= 0); - assert_eq!(MIN + MAX + 1, 0); + assert!(MIN + MAX + 1 == 0); } #[test] @@ -265,25 +265,25 @@ mod tests { #[test] pub fn test_abs() { - assert_eq!((1 as $T).abs(), 1 as $T); - assert_eq!((0 as $T).abs(), 0 as $T); - assert_eq!((-1 as $T).abs(), 1 as $T); + assert!((1 as $T).abs() == 1 as $T); + assert!((0 as $T).abs() == 0 as $T); + assert!((-1 as $T).abs() == 1 as $T); } #[test] fn test_abs_sub() { - assert_eq!((-1 as $T).abs_sub(&(1 as $T)), 0 as $T); - assert_eq!((1 as $T).abs_sub(&(1 as $T)), 0 as $T); - assert_eq!((1 as $T).abs_sub(&(0 as $T)), 1 as $T); - assert_eq!((1 as $T).abs_sub(&(-1 as $T)), 2 as $T); + assert!((-1 as $T).abs_sub(&(1 as $T)) == 0 as $T); + assert!((1 as $T).abs_sub(&(1 as $T)) == 0 as $T); + assert!((1 as $T).abs_sub(&(0 as $T)) == 1 as $T); + assert!((1 as $T).abs_sub(&(-1 as $T)) == 2 as $T); } #[test] fn test_signum() { - assert_eq!((1 as $T).signum(), 1 as $T); - assert_eq!((0 as $T).signum(), 0 as $T); - assert_eq!((-0 as $T).signum(), 0 as $T); - assert_eq!((-1 as $T).signum(), -1 as $T); + assert!((1 as $T).signum() == 1 as $T); + assert!((0 as $T).signum() == 0 as $T); + assert!((-0 as $T).signum() == 0 as $T); + assert!((-1 as $T).signum() == -1 as $T); } #[test] @@ -304,33 +304,33 @@ mod tests { #[test] fn test_bitwise() { - assert_eq!(0b1110 as $T, (0b1100 as $T).bitor(&(0b1010 as $T))); - assert_eq!(0b1000 as $T, (0b1100 as $T).bitand(&(0b1010 as $T))); - assert_eq!(0b0110 as $T, (0b1100 as $T).bitxor(&(0b1010 as $T))); - assert_eq!(0b1110 as $T, (0b0111 as $T).shl(&(1 as $T))); - assert_eq!(0b0111 as $T, (0b1110 as $T).shr(&(1 as $T))); - assert_eq!(-(0b11 as $T) - (1 as $T), (0b11 as $T).not()); + assert!(0b1110 as $T == (0b1100 as $T).bitor(&(0b1010 as $T))); + assert!(0b1000 as $T == (0b1100 as $T).bitand(&(0b1010 as $T))); + assert!(0b0110 as $T == (0b1100 as $T).bitxor(&(0b1010 as $T))); + assert!(0b1110 as $T == (0b0111 as $T).shl(&(1 as $T))); + assert!(0b0111 as $T == (0b1110 as $T).shr(&(1 as $T))); + assert!(-(0b11 as $T) - (1 as $T) == (0b11 as $T).not()); } #[test] fn test_count_ones() { - assert_eq!((0b0101100 as $T).count_ones(), 3); - assert_eq!((0b0100001 as $T).count_ones(), 2); - assert_eq!((0b1111001 as $T).count_ones(), 5); + assert!((0b0101100 as $T).count_ones() == 3); + assert!((0b0100001 as $T).count_ones() == 2); + assert!((0b1111001 as $T).count_ones() == 5); } #[test] fn test_count_zeros() { - assert_eq!((0b0101100 as $T).count_zeros(), BITS as $T - 3); - assert_eq!((0b0100001 as $T).count_zeros(), BITS as $T - 2); - assert_eq!((0b1111001 as $T).count_zeros(), BITS as $T - 5); + assert!((0b0101100 as $T).count_zeros() == BITS as $T - 3); + assert!((0b0100001 as $T).count_zeros() == BITS as $T - 2); + assert!((0b1111001 as $T).count_zeros() == BITS as $T - 5); } #[test] fn test_signed_checked_div() { - assert_eq!(10i.checked_div(&2), Some(5)); - assert_eq!(5i.checked_div(&0), None); - assert_eq!(int::MIN.checked_div(&-1), None); + assert!(10i.checked_div(&2) == Some(5)); + assert!(5i.checked_div(&0) == None); + assert!(int::MIN.checked_div(&-1) == None); } } diff --git a/src/libcore/num/uint_macros.rs b/src/libcore/num/uint_macros.rs index 3ef785a0462..12aaf41b196 100644 --- a/src/libcore/num/uint_macros.rs +++ b/src/libcore/num/uint_macros.rs @@ -192,7 +192,7 @@ mod tests { fn test_overflows() { assert!(MAX > 0); assert!(MIN <= 0); - assert_eq!(MIN + MAX + 1, 0); + assert!(MIN + MAX + 1 == 0); } #[test] @@ -202,32 +202,32 @@ mod tests { #[test] fn test_bitwise() { - assert_eq!(0b1110 as $T, (0b1100 as $T).bitor(&(0b1010 as $T))); - assert_eq!(0b1000 as $T, (0b1100 as $T).bitand(&(0b1010 as $T))); - assert_eq!(0b0110 as $T, (0b1100 as $T).bitxor(&(0b1010 as $T))); - assert_eq!(0b1110 as $T, (0b0111 as $T).shl(&(1 as $T))); - assert_eq!(0b0111 as $T, (0b1110 as $T).shr(&(1 as $T))); - assert_eq!(MAX - (0b1011 as $T), (0b1011 as $T).not()); + assert!(0b1110 as $T == (0b1100 as $T).bitor(&(0b1010 as $T))); + assert!(0b1000 as $T == (0b1100 as $T).bitand(&(0b1010 as $T))); + assert!(0b0110 as $T == (0b1100 as $T).bitxor(&(0b1010 as $T))); + assert!(0b1110 as $T == (0b0111 as $T).shl(&(1 as $T))); + assert!(0b0111 as $T == (0b1110 as $T).shr(&(1 as $T))); + assert!(MAX - (0b1011 as $T) == (0b1011 as $T).not()); } #[test] fn test_count_ones() { - assert_eq!((0b0101100 as $T).count_ones(), 3); - assert_eq!((0b0100001 as $T).count_ones(), 2); - assert_eq!((0b1111001 as $T).count_ones(), 5); + assert!((0b0101100 as $T).count_ones() == 3); + assert!((0b0100001 as $T).count_ones() == 2); + assert!((0b1111001 as $T).count_ones() == 5); } #[test] fn test_count_zeros() { - assert_eq!((0b0101100 as $T).count_zeros(), BITS as $T - 3); - assert_eq!((0b0100001 as $T).count_zeros(), BITS as $T - 2); - assert_eq!((0b1111001 as $T).count_zeros(), BITS as $T - 5); + assert!((0b0101100 as $T).count_zeros() == BITS as $T - 3); + assert!((0b0100001 as $T).count_zeros() == BITS as $T - 2); + assert!((0b1111001 as $T).count_zeros() == BITS as $T - 5); } #[test] fn test_unsigned_checked_div() { - assert_eq!(10u.checked_div(&2), Some(5)); - assert_eq!(5u.checked_div(&0), None); + assert!(10u.checked_div(&2) == Some(5)); + assert!(5u.checked_div(&0) == None); } } diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index e79e3056838..5b9c314d42b 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -70,7 +70,7 @@ use self::task::{Task, BlockedTask}; pub use self::util::default_sched_threads; // Export unwinding facilities used by the failure macros -pub use self::unwind::{begin_unwind, begin_unwind_raw, begin_unwind_fmt}; +pub use self::unwind::{begin_unwind, begin_unwind_fmt}; pub use self::util::{Stdio, Stdout, Stderr}; diff --git a/src/libstd/rt/unwind.rs b/src/libstd/rt/unwind.rs index 3ba97f381ab..5f3731eb819 100644 --- a/src/libstd/rt/unwind.rs +++ b/src/libstd/rt/unwind.rs @@ -58,7 +58,6 @@ // Currently Rust uses unwind runtime provided by libgcc. use any::{Any, AnyRefExt}; -use c_str::CString; use cast; use fmt; use kinds::Send; @@ -298,42 +297,23 @@ pub mod eabi { } #[cold] -#[lang="fail_"] +#[no_mangle] #[cfg(not(test))] -pub fn fail_(expr: *u8, file: *u8, line: uint) -> ! { - begin_unwind_raw(expr, file, line); -} - -#[cold] -#[lang="fail_bounds_check"] -#[cfg(not(test))] -pub fn fail_bounds_check(file: *u8, line: uint, index: uint, len: uint) -> ! { - use c_str::ToCStr; +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); - msg.with_c_str(|buf| fail_(buf as *u8, file, line)) + begin_unwind(msg, unsafe { c_str_to_static_slice(file as *i8) }, line) } -/// This is the entry point of unwinding for things like lang items and such. -/// The arguments are normally generated by the compiler, and need to -/// have static lifetimes. -#[inline(never)] #[cold] // this is the slow path, please never inline this -pub fn begin_unwind_raw(msg: *u8, file: *u8, line: uint) -> ! { - use libc::c_char; - #[inline] - fn static_char_ptr(p: *u8) -> &'static str { - let s = unsafe { CString::new(p as *c_char, false) }; - match s.as_str() { - Some(s) => unsafe { cast::transmute::<&str, &'static str>(s) }, - None => rtabort!("message wasn't utf8?") - } - } - - let msg = static_char_ptr(msg); - let file = static_char_ptr(file); - - begin_unwind(msg, file, line as uint) +// 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) } /// The entry point for unwinding with a formatted message. -- cgit 1.4.1-3-g733a5 From d4b5d82a3356630ede4ce1b436cb59760be7b703 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 1 May 2014 11:12:16 -0700 Subject: core: Add unwrap()/unwrap_err() methods to Result These implementations must live in libstd right now because the fmt module has not been migrated yet. This will occur in a later PR. Just to be clear, there are new extension traits, but they are not necessary once the std::fmt module has migrated to libcore, which is a planned migration in the future. --- src/libstd/fmt/num.rs | 2 + src/libstd/io/buffered.rs | 2 +- src/libstd/io/fs.rs | 2 +- src/libstd/io/net/udp.rs | 2 + src/libstd/io/stdio.rs | 2 +- src/libstd/io/tempfile.rs | 2 +- src/libstd/num/strconv.rs | 3 + src/libstd/prelude.rs | 1 + src/libstd/repr.rs | 2 +- src/libstd/result.rs | 312 ++++++++++++++++++++++++++++++++++++++++++++++ src/libstd/rt/task.rs | 2 +- src/libstd/task.rs | 1 + 12 files changed, 327 insertions(+), 6 deletions(-) create mode 100644 src/libstd/result.rs (limited to 'src/libstd/rt') diff --git a/src/libstd/fmt/num.rs b/src/libstd/fmt/num.rs index 12adcee2f0f..839b7407e55 100644 --- a/src/libstd/fmt/num.rs +++ b/src/libstd/fmt/num.rs @@ -400,6 +400,7 @@ mod bench { use super::test::Bencher; use fmt::radix; use rand::{XorShiftRng, Rng}; + use realstd::result::ResultUnwrap; #[bench] fn format_bin(b: &mut Bencher) { @@ -436,6 +437,7 @@ mod bench { use super::test::Bencher; use fmt::radix; use rand::{XorShiftRng, Rng}; + use realstd::result::ResultUnwrap; #[bench] fn format_bin(b: &mut Bencher) { diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs index 298e9df6d68..a8e7b324bd7 100644 --- a/src/libstd/io/buffered.rs +++ b/src/libstd/io/buffered.rs @@ -16,7 +16,7 @@ use io::{Reader, Writer, Stream, Buffer, DEFAULT_BUF_SIZE, IoResult}; use iter::ExactSize; use ops::Drop; use option::{Some, None, Option}; -use result::{Ok, Err}; +use result::{Ok, Err, ResultUnwrap}; use slice::{ImmutableVector, MutableVector}; use slice; use vec::Vec; diff --git a/src/libstd/io/fs.rs b/src/libstd/io/fs.rs index 3f66ecd5db3..125b4ddad88 100644 --- a/src/libstd/io/fs.rs +++ b/src/libstd/io/fs.rs @@ -1335,7 +1335,7 @@ mod test { use rand::{StdRng, Rng}; let mut bytes = [0, ..1024]; - StdRng::new().unwrap().fill_bytes(bytes); + StdRng::new().ok().unwrap().fill_bytes(bytes); let tmpdir = tmpdir(); diff --git a/src/libstd/io/net/udp.rs b/src/libstd/io/net/udp.rs index b5b0cf7bede..b7636493dec 100644 --- a/src/libstd/io/net/udp.rs +++ b/src/libstd/io/net/udp.rs @@ -358,6 +358,8 @@ mod test { }) pub fn socket_name(addr: SocketAddr) { + use result::ResultUnwrap; + let server = UdpSocket::bind(addr); assert!(server.is_ok()); diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 613e9f027a4..69ba0fb20ee 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -36,7 +36,7 @@ use mem::replace; use option::{Option, Some, None}; use owned::Box; use prelude::drop; -use result::{Ok, Err}; +use result::{Ok, Err, ResultUnwrap}; use rt; use rt::local::Local; use rt::rtio::{DontClose, IoFactory, LocalIo, RtioFileStream, RtioTTY}; diff --git a/src/libstd/io/tempfile.rs b/src/libstd/io/tempfile.rs index 4ff1c7faaec..8c28caa988a 100644 --- a/src/libstd/io/tempfile.rs +++ b/src/libstd/io/tempfile.rs @@ -18,7 +18,7 @@ use ops::Drop; use option::{Option, None, Some}; use os; use path::{Path, GenericPath}; -use result::{Ok, Err}; +use result::{Ok, Err, ResultUnwrap}; use sync::atomics; /// A wrapper for a path to temporary directory implementing automatic diff --git a/src/libstd/num/strconv.rs b/src/libstd/num/strconv.rs index bb2fd2a4e25..8861597bb4c 100644 --- a/src/libstd/num/strconv.rs +++ b/src/libstd/num/strconv.rs @@ -820,6 +820,7 @@ mod bench { use super::test::Bencher; use rand::{XorShiftRng, Rng}; use num::ToStrRadix; + use realstd::result::ResultUnwrap; #[bench] fn to_str_bin(b: &mut Bencher) { @@ -856,6 +857,7 @@ mod bench { use super::test::Bencher; use rand::{XorShiftRng, Rng}; use num::ToStrRadix; + use realstd::result::ResultUnwrap; #[bench] fn to_str_bin(b: &mut Bencher) { @@ -892,6 +894,7 @@ mod bench { use super::test::Bencher; use rand::{XorShiftRng, Rng}; use f64; + use realstd::result::ResultUnwrap; #[bench] fn float_to_str(b: &mut Bencher) { diff --git a/src/libstd/prelude.rs b/src/libstd/prelude.rs index a8e26845c1b..75eb56ffa52 100644 --- a/src/libstd/prelude.rs +++ b/src/libstd/prelude.rs @@ -69,6 +69,7 @@ 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/repr.rs b/src/libstd/repr.rs index 380951772ae..2fe4c7eafde 100644 --- a/src/libstd/repr.rs +++ b/src/libstd/repr.rs @@ -25,7 +25,7 @@ use option::{Some, None, Option}; use ptr::RawPtr; use reflect; use reflect::{MovePtr, align}; -use result::{Ok, Err}; +use result::{Ok, Err, ResultUnwrap}; use str::StrSlice; use to_str::ToStr; use slice::Vector; diff --git a/src/libstd/result.rs b/src/libstd/result.rs new file mode 100644 index 00000000000..cc9e6684d28 --- /dev/null +++ b/src/libstd/result.rs @@ -0,0 +1,312 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Error handling with the `Result` type +//! +//! `Result` 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 { +//! 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 { +//! 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 = Ok(10); +//! let bad_result: Result = 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 = good_result.map(|i| i + 1); +//! let bad_result: Result = bad_result.map(|i| i - 1); +//! +//! // Use `and_then` to continue the computation. +//! let good_result: Result = good_result.and_then(|i| Ok(i == 11)); +//! +//! // Use `or_else` to handle the error. +//! let bad_result: Result = 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`.* +//! +//! 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`](enum.Result.html#method.ok) and +//! [`err`](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`. +//! +//! 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 { + /// 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 { + /// Unwraps a result, yielding the content of an `Err`. + /// + /// Fails if the value is an `Ok`. + fn unwrap_err(self) -> E; +} + +impl ResultUnwrap for Result { + #[inline] + fn unwrap(self) -> T { + match self { + Ok(t) => t, + Err(e) => + fail!("called `Result::unwrap()` on an `Err` value: {}", e) + } + } +} + +impl ResultUnwrapErr for Result { + #[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/task.rs b/src/libstd/rt/task.rs index 5b29de5a8c1..909df5618aa 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -436,7 +436,7 @@ mod test { #[test] fn rng() { use rand::{StdRng, Rng}; - let mut r = StdRng::new().unwrap(); + let mut r = StdRng::new().ok().unwrap(); let _ = r.next_u32(); } diff --git a/src/libstd/task.rs b/src/libstd/task.rs index 23831e40a8b..2f7b31ae31d 100644 --- a/src/libstd/task.rs +++ b/src/libstd/task.rs @@ -50,6 +50,7 @@ use str::{Str, SendStr, IntoMaybeOwned}; #[cfg(test)] use any::{AnyOwnExt, AnyRefExt}; #[cfg(test)] use result; #[cfg(test)] use str::StrAllocating; +#[cfg(test)] use realstd::result::ResultUnwrap; /// Indicates the manner in which a task exited. /// -- cgit 1.4.1-3-g733a5 From 0d8f5fa618da00653897be2050980c800389be82 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 6 May 2014 21:25:36 -0700 Subject: core: Move Option::expect to libstd from libcore See #14008 for more details --- src/libcore/option.rs | 24 ------ src/libcore/should_not_exist.rs | 10 +-- src/libstd/io/timer.rs | 1 + src/libstd/lib.rs | 3 +- src/libstd/option.rs | 167 ++++++++++++++++++++++++++++++++++++++++ src/libstd/prelude.rs | 1 + src/libstd/rt/env.rs | 2 +- src/libstd/vec.rs | 2 +- 8 files changed, 177 insertions(+), 33 deletions(-) create mode 100644 src/libstd/option.rs (limited to 'src/libstd/rt') diff --git a/src/libcore/option.rs b/src/libcore/option.rs index 143e8bf2622..c6884a8002f 100644 --- a/src/libcore/option.rs +++ b/src/libcore/option.rs @@ -230,30 +230,6 @@ impl Option { // Getting to contained values ///////////////////////////////////////////////////////////////////////// - /// Unwraps an option, yielding the content of a `Some` - /// - /// # Failure - /// - /// Fails if the value is a `None` with a custom failure message provided by `msg`. - #[inline] - #[cfg(not(test))] - pub fn expect(self, msg: &str) -> T { - match self { - Some(val) => val, - None => fail!(msg), - } - } - - // FIXME: once std::fmt is in libcore, this extra variant should not be - // necessary. - #[cfg(test)] - pub fn expect(self, msg: &str) -> T { - match self { - Some(val) => val, - None => fail!("{}", msg), - } - } - /// Moves a value out of an option type and returns it, consuming the `Option`. /// /// # Failure diff --git a/src/libcore/should_not_exist.rs b/src/libcore/should_not_exist.rs index 587723563eb..50447f0c5b3 100644 --- a/src/libcore/should_not_exist.rs +++ b/src/libcore/should_not_exist.rs @@ -34,7 +34,7 @@ extern { } unsafe fn alloc(cap: uint) -> *mut Vec<()> { - let cap = cap.checked_add(&mem::size_of::>()).expect("cap overflow"); + let cap = cap.checked_add(&mem::size_of::>()).unwrap(); let ret = malloc(cap) as *mut Vec<()>; if ret.is_null() { intrinsics::abort(); @@ -94,7 +94,7 @@ impl FromIterator for ~str { let amt = ch.encode_utf8(tmp); if len + amt > cap { - cap = cap.checked_mul(&2).expect("cap overflow"); + cap = cap.checked_mul(&2).unwrap(); if cap < len + amt { cap = len + amt; } @@ -124,7 +124,7 @@ impl FromIterator for ~str { impl<'a> Add<&'a str,~str> for &'a str { #[inline] fn add(&self, rhs: & &'a str) -> ~str { - let amt = self.len().checked_add(&rhs.len()).expect("len overflow"); + let amt = self.len().checked_add(&rhs.len()).unwrap(); unsafe { let ptr = alloc(amt) as *mut Vec; let base = &mut (*ptr).data as *mut _; @@ -155,7 +155,7 @@ impl FromIterator for ~[A] { fn from_iter>(mut iterator: T) -> ~[A] { let (lower, _) = iterator.size_hint(); let cap = if lower == 0 {16} else {lower}; - let mut cap = cap.checked_mul(&mem::size_of::()).expect("cap overflow"); + let mut cap = cap.checked_mul(&mem::size_of::()).unwrap(); let mut len = 0; unsafe { @@ -163,7 +163,7 @@ impl FromIterator for ~[A] { let mut ret = cast::transmute(ptr); for elt in iterator { if len * mem::size_of::() >= cap { - cap = cap.checked_mul(&2).expect("cap overflow"); + cap = cap.checked_mul(&2).unwrap(); let ptr2 = alloc(cap) as *mut Vec; ptr::copy_nonoverlapping_memory(&mut (*ptr2).data, &(*ptr).data, diff --git a/src/libstd/io/timer.rs b/src/libstd/io/timer.rs index 96c4083e7ed..5565918ef85 100644 --- a/src/libstd/io/timer.rs +++ b/src/libstd/io/timer.rs @@ -21,6 +21,7 @@ use comm::Receiver; use io::IoResult; use kinds::Send; use owned::Box; +use option::Expect; use rt::rtio::{IoFactory, LocalIo, RtioTimer}; /// A synchronous timer object diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 4a555035a08..abcc1f099b4 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -149,7 +149,6 @@ pub use core::default; pub use core::intrinsics; pub use core::iter; pub use core::mem; -pub use core::option; pub use core::ptr; pub use core::raw; pub use core::tuple; @@ -221,7 +220,7 @@ pub mod hash; /* Common data structures */ pub mod result; - +pub mod option; /* Tasks and communication */ diff --git a/src/libstd/option.rs b/src/libstd/option.rs new file mode 100644 index 00000000000..17249f567e5 --- /dev/null +++ b/src/libstd/option.rs @@ -0,0 +1,167 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Optional values +//! +//! Type `Option` represents an optional value: every `Option` +//! is either `Some` and contains a value, or `None`, and +//! does not. `Option` types are very common in Rust code, as +//! they have a number of uses: +//! +//! * Initial values +//! * Return values for functions that are not defined +//! over their entire input range (partial functions) +//! * Return value for otherwise reporting simple errors, where `None` is +//! returned on error +//! * Optional struct fields +//! * Struct fields that can be loaned or "taken" +//! * Optional function arguments +//! * Nullable pointers +//! * Swapping things out of difficult situations +//! +//! Options are commonly paired with pattern matching to query the presence +//! of a value and take action, always accounting for the `None` case. +//! +//! ``` +//! # // FIXME This is not the greatest first example +//! // cow_says contains the word "moo" +//! let cow_says = Some("moo"); +//! // dog_says does not contain a value +//! let dog_says: Option<&str> = None; +//! +//! // Pattern match to retrieve the value +//! match (cow_says, dog_says) { +//! (Some(cow_words), Some(dog_words)) => { +//! println!("Cow says {} and dog says {}!", cow_words, dog_words); +//! } +//! (Some(cow_words), None) => println!("Cow says {}", cow_words), +//! (None, Some(dog_words)) => println!("Dog says {}", dog_words), +//! (None, None) => println!("Cow and dog are suspiciously silent") +//! } +//! ``` +//! +// +// FIXME: Show how `Option` is used in practice, with lots of methods +// +//! # Options and pointers ("nullable" pointers) +//! +//! Rust's pointer types must always point to a valid location; there are +//! no "null" pointers. Instead, Rust has *optional* pointers, like +//! the optional owned box, `Option<~T>`. +//! +//! The following example uses `Option` to create an optional box of +//! `int`. Notice that in order to use the inner `int` value first the +//! `check_optional` function needs to use pattern matching to +//! determine whether the box has a value (i.e. it is `Some(...)`) or +//! not (`None`). +//! +//! ``` +//! let optional: Option<~int> = None; +//! check_optional(&optional); +//! +//! let optional: Option<~int> = Some(~9000); +//! check_optional(&optional); +//! +//! fn check_optional(optional: &Option<~int>) { +//! match *optional { +//! Some(ref p) => println!("have value {}", p), +//! None => println!("have no value") +//! } +//! } +//! ``` +//! +//! This usage of `Option` to create safe nullable pointers is so +//! common that Rust does special optimizations to make the +//! representation of `Option<~T>` a single pointer. Optional pointers +//! in Rust are stored as efficiently as any other pointer type. +//! +//! # Examples +//! +//! Basic pattern matching on `Option`: +//! +//! ``` +//! let msg = Some("howdy"); +//! +//! // Take a reference to the contained string +//! match msg { +//! Some(ref m) => println!("{}", *m), +//! None => () +//! } +//! +//! // Remove the contained string, destroying the Option +//! let unwrapped_msg = match msg { +//! Some(m) => m, +//! None => "default message" +//! }; +//! ``` +//! +//! Initialize a result to `None` before a loop: +//! +//! ``` +//! enum Kingdom { Plant(uint, &'static str), Animal(uint, &'static str) } +//! +//! // A list of data to search through. +//! let all_the_big_things = [ +//! Plant(250, "redwood"), +//! Plant(230, "noble fir"), +//! Plant(229, "sugar pine"), +//! Animal(25, "blue whale"), +//! Animal(19, "fin whale"), +//! Animal(15, "north pacific right whale"), +//! ]; +//! +//! // We're going to search for the name of the biggest animal, +//! // but to start with we've just got `None`. +//! let mut name_of_biggest_animal = None; +//! let mut size_of_biggest_animal = 0; +//! for big_thing in all_the_big_things.iter() { +//! match *big_thing { +//! Animal(size, name) if size > size_of_biggest_animal => { +//! // Now we've found the name of some big animal +//! size_of_biggest_animal = size; +//! name_of_biggest_animal = Some(name); +//! } +//! Animal(..) | Plant(..) => () +//! } +//! } +//! +//! match name_of_biggest_animal { +//! Some(name) => println!("the biggest animal is {}", name), +//! None => println!("there are no animals :(") +//! } +//! ``` + +use any::Any; +use kinds::Send; + +pub use core::option::{Option, Some, None, Item, collect}; + +/// Extension trait for the `Option` type to add an `expect` method + +// FIXME(#14008) should this trait even exist? +pub trait Expect { + /// Unwraps an option, yielding the content of a `Some` + /// + /// # Failure + /// + /// Fails if the value is a `None` with a custom failure message provided by + /// `msg`. + fn expect(self, m: M) -> T; +} + +impl Expect for Option { + #[inline] + fn expect(self, msg: M) -> T { + match self { + Some(val) => val, + None => fail!(msg), + } + } +} diff --git a/src/libstd/prelude.rs b/src/libstd/prelude.rs index 75eb56ffa52..6cd9e96496f 100644 --- a/src/libstd/prelude.rs +++ b/src/libstd/prelude.rs @@ -65,6 +65,7 @@ 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 option::Expect; pub use owned::Box; pub use path::{GenericPath, Path, PosixPath, WindowsPath}; pub use ptr::RawPtr; diff --git a/src/libstd/rt/env.rs b/src/libstd/rt/env.rs index 94f56d42613..708c42030ab 100644 --- a/src/libstd/rt/env.rs +++ b/src/libstd/rt/env.rs @@ -11,7 +11,7 @@ //! Runtime environment settings use from_str::from_str; -use option::{Some, None}; +use option::{Some, None, Expect}; use os; // Note that these are all accessed without any synchronization. diff --git a/src/libstd/vec.rs b/src/libstd/vec.rs index 35eb5359311..af146b96e50 100644 --- a/src/libstd/vec.rs +++ b/src/libstd/vec.rs @@ -23,7 +23,7 @@ use mem; use num; use num::{CheckedMul, CheckedAdd}; use ops::Drop; -use option::{None, Option, Some}; +use option::{None, Option, Some, Expect}; use ptr::RawPtr; use ptr; use rt::global_heap::{malloc_raw, realloc_raw}; -- cgit 1.4.1-3-g733a5