auto merge of #8294 : erickt/rust/map-move, r=bblum

According to #7887, we've decided to use the syntax of `fn map<U>(f: &fn(&T) -> U) -> U`, which passes a reference to the closure, and to `fn map_move<U>(f: &fn(T) -> U) -> U` which moves the value into the closure. This PR adds these `.map_move()` functions to `Option` and `Result`.

In addition, it has these other minor features:
 
* Replaces a couple uses of `option.get()`, `result.get()`, and `result.get_err()` with `option.unwrap()`, `result.unwrap()`, and `result.unwrap_err()`. (See #8268 and #8288 for a more thorough adaptation of this functionality.
* Removes `option.take_map()` and `option.take_map_default()`. These two functions can be easily written as `.take().map_move(...)`.
* Adds a better error message to `result.unwrap()` and `result.unwrap_err()`.
This commit is contained in:
bors 2013-08-07 13:23:07 -07:00
commit 98ec79c957
58 changed files with 243 additions and 211 deletions

View file

@ -238,7 +238,7 @@ impl<K:Hash + Eq,V> HashMap<K, V> {
let len_buckets = self.buckets.len();
let bucket = self.buckets[idx].take();
let value = do bucket.map_consume |bucket| {
let value = do bucket.map_move |bucket| {
bucket.value
};
@ -479,7 +479,7 @@ impl<K: Hash + Eq, V> HashMap<K, V> {
impl<K: Hash + Eq, V: Clone> HashMap<K, V> {
/// Like `find`, but returns a copy of the value.
pub fn find_copy(&self, k: &K) -> Option<V> {
self.find(k).map_consume(|v| (*v).clone())
self.find(k).map_move(|v| (*v).clone())
}
/// Like `get`, but returns a copy of the value.

View file

@ -674,7 +674,7 @@ impl<A, T: Iterator<A>> IteratorUtil<A> for T {
Some((y, y_val))
}
}
}).map_consume(|(x, _)| x)
}).map_move(|(x, _)| x)
}
#[inline]
@ -689,7 +689,7 @@ impl<A, T: Iterator<A>> IteratorUtil<A> for T {
Some((y, y_val))
}
}
}).map_consume(|(x, _)| x)
}).map_move(|(x, _)| x)
}
}
@ -1382,7 +1382,7 @@ impl<'self, A, T: Iterator<A>, B, U: Iterator<B>> Iterator<B> for
return Some(x)
}
}
match self.iter.next().map_consume(|x| (self.f)(x)) {
match self.iter.next().map_move(|x| (self.f)(x)) {
None => return self.backiter.chain_mut_ref(|it| it.next()),
next => self.frontiter = next,
}
@ -1414,7 +1414,7 @@ impl<'self,
y => return y
}
}
match self.iter.next_back().map_consume(|x| (self.f)(x)) {
match self.iter.next_back().map_move(|x| (self.f)(x)) {
None => return self.frontiter.chain_mut_ref(|it| it.next_back()),
next => self.backiter = next,
}

View file

@ -110,16 +110,16 @@ fn test_tls_multitask() {
set(my_key, @~"parent data");
do task::spawn {
// TLS shouldn't carry over.
assert!(get(my_key, |k| k.map(|&k| *k)).is_none());
assert!(get(my_key, |k| k.map_move(|k| *k)).is_none());
set(my_key, @~"child data");
assert!(*(get(my_key, |k| k.map(|&k| *k)).unwrap()) ==
assert!(*(get(my_key, |k| k.map_move(|k| *k)).unwrap()) ==
~"child data");
// should be cleaned up for us
}
// Must work multiple times
assert!(*(get(my_key, |k| k.map(|&k| *k)).unwrap()) == ~"parent data");
assert!(*(get(my_key, |k| k.map(|&k| *k)).unwrap()) == ~"parent data");
assert!(*(get(my_key, |k| k.map(|&k| *k)).unwrap()) == ~"parent data");
assert!(*(get(my_key, |k| k.map_move(|k| *k)).unwrap()) == ~"parent data");
assert!(*(get(my_key, |k| k.map_move(|k| *k)).unwrap()) == ~"parent data");
assert!(*(get(my_key, |k| k.map_move(|k| *k)).unwrap()) == ~"parent data");
}
#[test]
@ -127,7 +127,7 @@ fn test_tls_overwrite() {
static my_key: Key<@~str> = &Key;
set(my_key, @~"first data");
set(my_key, @~"next data"); // Shouldn't leak.
assert!(*(get(my_key, |k| k.map(|&k| *k)).unwrap()) == ~"next data");
assert!(*(get(my_key, |k| k.map_move(|k| *k)).unwrap()) == ~"next data");
}
#[test]

View file

@ -208,6 +208,12 @@ impl<T> Option<T> {
match *self { Some(ref mut x) => Some(f(x)), None => None }
}
/// Applies a function to the contained value or returns a default
#[inline]
pub fn map_default<'a, U>(&'a self, def: U, f: &fn(&'a T) -> U) -> U {
match *self { None => def, Some(ref t) => f(t) }
}
/// Maps a `Some` value from one type to another by a mutable reference,
/// or returns a default value.
#[inline]
@ -218,21 +224,15 @@ impl<T> Option<T> {
/// As `map`, but consumes the option and gives `f` ownership to avoid
/// copying.
#[inline]
pub fn map_consume<U>(self, f: &fn(v: T) -> U) -> Option<U> {
match self { None => None, Some(v) => Some(f(v)) }
}
/// Applies a function to the contained value or returns a default
#[inline]
pub fn map_default<'a, U>(&'a self, def: U, f: &fn(&'a T) -> U) -> U {
match *self { None => def, Some(ref t) => f(t) }
pub fn map_move<U>(self, f: &fn(T) -> U) -> Option<U> {
match self { Some(x) => Some(f(x)), None => None }
}
/// As `map_default`, but consumes the option and gives `f`
/// ownership to avoid copying.
#[inline]
pub fn map_consume_default<U>(self, def: U, f: &fn(v: T) -> U) -> U {
match self { None => def, Some(v) => f(v) }
pub fn map_move_default<U>(self, def: U, f: &fn(T) -> U) -> U {
match self { None => def, Some(t) => f(t) }
}
/// Take the value out of the option, leaving a `None` in its place.
@ -241,20 +241,6 @@ impl<T> Option<T> {
util::replace(self, None)
}
/// As `map_consume`, but swaps a None into the original option rather
/// than consuming it by-value.
#[inline]
pub fn take_map<U>(&mut self, blk: &fn(T) -> U) -> Option<U> {
self.take().map_consume(blk)
}
/// As `map_consume_default`, but swaps a None into the original option
/// rather than consuming it by-value.
#[inline]
pub fn take_map_default<U> (&mut self, def: U, blk: &fn(T) -> U) -> U {
self.take().map_consume_default(def, blk)
}
/// Apply a function to the contained value or do nothing.
/// Returns true if the contained value was mutated.
pub fn mutate(&mut self, f: &fn(T) -> T) -> bool {

View file

@ -498,9 +498,7 @@ pub fn self_exe_path() -> Option<Path> {
}
}
do load_self().map |pth| {
Path(*pth).dir_path()
}
load_self().map_move(|path| Path(path).dir_path())
}

View file

@ -149,6 +149,40 @@ impl<T, E: ToStr> Result<T, E> {
}
}
/// Call a method based on a previous result
///
/// If `self` is `Ok` then the value is extracted and passed to `op`
/// whereupon `op`s result is wrapped in `Ok` and returned. if `self` is
/// `Err` then it is immediately returned. This function can be used to
/// compose the results of two functions.
///
/// Example:
///
/// let res = do read_file(file).map_move |buf| {
/// parse_bytes(buf)
/// }
#[inline]
pub fn map_move<U>(self, op: &fn(T) -> U) -> Result<U,E> {
match self {
Ok(t) => Ok(op(t)),
Err(e) => Err(e)
}
}
/// Call a method based on a previous result
///
/// If `self` is `Err` then the value is extracted and passed to `op`
/// whereupon `op`s result is wrapped in an `Err` and returned. if `self` is
/// `Ok` then it is immediately returned. This function can be used to pass
/// through a successful result while handling an error.
#[inline]
pub fn map_err_move<F>(self, op: &fn(E) -> F) -> Result<T,F> {
match self {
Ok(t) => Ok(t),
Err(e) => Err(op(e))
}
}
/// Call a method based on a previous result
///
/// If `self` is `Ok` then the value is extracted and passed to `op`
@ -312,7 +346,9 @@ pub fn iter_vec2<S, T, U: ToStr>(ss: &[S], ts: &[T],
#[cfg(test)]
mod tests {
use super::*;
use either;
use str::OwnedStr;
pub fn op1() -> Result<int, ~str> { Ok(666) }
@ -359,14 +395,26 @@ mod tests {
#[test]
pub fn test_impl_map() {
assert_eq!(Ok::<~str, ~str>(~"a").map(|_x| ~"b"), Ok(~"b"));
assert_eq!(Err::<~str, ~str>(~"a").map(|_x| ~"b"), Err(~"a"));
assert_eq!(Ok::<~str, ~str>(~"a").map(|x| (~"b").append(*x)), Ok(~"ba"));
assert_eq!(Err::<~str, ~str>(~"a").map(|x| (~"b").append(*x)), Err(~"a"));
}
#[test]
pub fn test_impl_map_err() {
assert_eq!(Ok::<~str, ~str>(~"a").map_err(|_x| ~"b"), Ok(~"a"));
assert_eq!(Err::<~str, ~str>(~"a").map_err(|_x| ~"b"), Err(~"b"));
assert_eq!(Ok::<~str, ~str>(~"a").map_err(|x| (~"b").append(*x)), Ok(~"a"));
assert_eq!(Err::<~str, ~str>(~"a").map_err(|x| (~"b").append(*x)), Err(~"ba"));
}
#[test]
pub fn test_impl_map_move() {
assert_eq!(Ok::<~str, ~str>(~"a").map_move(|x| x + "b"), Ok(~"ab"));
assert_eq!(Err::<~str, ~str>(~"a").map_move(|x| x + "b"), Err(~"a"));
}
#[test]
pub fn test_impl_map_err_move() {
assert_eq!(Ok::<~str, ~str>(~"a").map_err_move(|x| x + "b"), Ok(~"a"));
assert_eq!(Err::<~str, ~str>(~"a").map_err_move(|x| x + "b"), Err(~"ab"));
}
#[test]

View file

@ -159,7 +159,7 @@ impl<T> ChanOne<T> {
// Port is blocked. Wake it up.
let recvr = BlockedTask::cast_from_uint(task_as_state);
if do_resched {
do recvr.wake().map_consume |woken_task| {
do recvr.wake().map_move |woken_task| {
Scheduler::run_task(woken_task);
};
} else {
@ -381,7 +381,7 @@ impl<T> Drop for ChanOne<T> {
// The port is blocked waiting for a message we will never send. Wake it.
assert!((*this.packet()).payload.is_none());
let recvr = BlockedTask::cast_from_uint(task_as_state);
do recvr.wake().map_consume |woken_task| {
do recvr.wake().map_move |woken_task| {
Scheduler::run_task(woken_task);
};
}

View file

@ -402,10 +402,10 @@ impl KillHandle {
|| {
// Prefer to check tombstones that were there first,
// being "more fair" at the expense of tail-recursion.
others.take().map_consume_default(true, |f| f()) && {
others.take().map_move_default(true, |f| f()) && {
let mut inner = this.take().unwrap();
(!inner.any_child_failed) &&
inner.child_tombstones.take_map_default(true, |f| f())
inner.child_tombstones.take().map_move_default(true, |f| f())
}
}
}
@ -424,7 +424,7 @@ impl KillHandle {
let others = Cell::new(other_tombstones); // :(
|| {
// Prefer fairness to tail-recursion, as in above case.
others.take().map_consume_default(true, |f| f()) &&
others.take().map_move_default(true, |f| f()) &&
f.take()()
}
}
@ -493,7 +493,7 @@ impl Death {
{ use util; util::ignore(group); }
// Step 1. Decide if we need to collect child failures synchronously.
do self.on_exit.take_map |on_exit| {
do self.on_exit.take().map_move |on_exit| {
if success {
// We succeeded, but our children might not. Need to wait for them.
let mut inner = self.kill_handle.take_unwrap().unwrap();
@ -501,7 +501,7 @@ impl Death {
success = false;
} else {
// Lockless access to tombstones protected by unwrap barrier.
success = inner.child_tombstones.take_map_default(true, |f| f());
success = inner.child_tombstones.take().map_move_default(true, |f| f());
}
}
on_exit(success);
@ -510,12 +510,12 @@ impl Death {
// Step 2. Possibly alert possibly-watching parent to failure status.
// Note that as soon as parent_handle goes out of scope, the parent
// can successfully unwrap its handle and collect our reported status.
do self.watching_parent.take_map |mut parent_handle| {
do self.watching_parent.take().map_move |mut parent_handle| {
if success {
// Our handle might be None if we had an exit callback, and
// already unwrapped it. But 'success' being true means no
// child failed, so there's nothing to do (see below case).
do self.kill_handle.take_map |own_handle| {
do self.kill_handle.take().map_move |own_handle| {
own_handle.reparent_children_to(&mut parent_handle);
};
} else {

View file

@ -325,7 +325,7 @@ impl Scheduler {
/// As enqueue_task, but with the possibility for the blocked task to
/// already have been killed.
pub fn enqueue_blocked_task(&mut self, blocked_task: BlockedTask) {
do blocked_task.wake().map_consume |task| {
do blocked_task.wake().map_move |task| {
self.enqueue_task(task);
};
}
@ -533,7 +533,7 @@ impl Scheduler {
sched.enqueue_blocked_task(last_task);
}
};
opt.map_consume(Local::put);
opt.map_move(Local::put);
}
// The primary function for changing contexts. In the current

View file

@ -465,10 +465,10 @@ mod test {
do run_in_newsched_task() {
static key: local_data::Key<@~str> = &local_data::Key;
local_data::set(key, @~"data");
assert!(*local_data::get(key, |k| k.map(|&k| *k)).unwrap() == ~"data");
assert!(*local_data::get(key, |k| k.map_move(|k| *k)).unwrap() == ~"data");
static key2: local_data::Key<@~str> = &local_data::Key;
local_data::set(key2, @~"data");
assert!(*local_data::get(key2, |k| k.map(|&k| *k)).unwrap() == ~"data");
assert!(*local_data::get(key2, |k| k.map_move(|k| *k)).unwrap() == ~"data");
}
}

View file

@ -1849,7 +1849,7 @@ impl<'self> StrSlice<'self> for &'self str {
} else {
self.matches_index_iter(needle)
.next()
.map_consume(|(start, _end)| start)
.map_move(|(start, _end)| start)
}
}

View file

@ -500,7 +500,7 @@ impl RuntimeGlue {
OldTask(ptr) => rt::rust_task_kill_other(ptr),
NewTask(handle) => {
let mut handle = handle;
do handle.kill().map_consume |killed_task| {
do handle.kill().map_move |killed_task| {
let killed_task = Cell::new(killed_task);
do Local::borrow::<Scheduler, ()> |sched| {
sched.enqueue_task(killed_task.take());
@ -682,7 +682,7 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) {
// Child task runs this code.
// If child data is 'None', the enlist is vacuously successful.
let enlist_success = do child_data.take().map_consume_default(true) |child_data| {
let enlist_success = do child_data.take().map_move_default(true) |child_data| {
let child_data = Cell::new(child_data); // :(
do Local::borrow::<Task, bool> |me| {
let (child_tg, ancestors, is_main) = child_data.take();
@ -854,7 +854,7 @@ fn spawn_raw_oldsched(mut opts: TaskOpts, f: ~fn()) {
// Even if the below code fails to kick the child off, we must
// send Something on the notify channel.
let notifier = notify_chan.map_consume(|c| AutoNotify(c));
let notifier = notify_chan.map_move(|c| AutoNotify(c));
if enlist_many(OldTask(child), &child_arc, &mut ancestors) {
let group = @@mut Taskgroup(child_arc, ancestors, is_main, notifier);