/*! * Operations on the ubiquitous `option` type. * * Type `option` represents an optional value. * * Every `Option` value can either be `Some(T)` or `none`. Where in other * languages you might use a nullable type, in Rust you would use an option * type. */ // NB: transitionary, de-mode-ing. #[warn(deprecated_mode)]; #[forbid(deprecated_pattern)]; use cmp::Eq; /// The option type enum Option { None, Some(T), } pure fn get(opt: &Option) -> T { /*! * Gets the value out of an option * * # Failure * * Fails if the value equals `none` */ match *opt { Some(x) => return x, None => fail ~"option::get none" } } pure fn get_ref(opt: &r/Option) -> &r/T { /*! * Gets an immutable reference to the value inside an option. * * # Failure * * Fails if the value equals `none` */ match *opt { Some(ref x) => x, None => fail ~"option::get_ref none" } } pure fn expect(opt: &Option, +reason: ~str) -> T { /*! * Gets the value out of an option, printing a specified message on * failure * * # Failure * * Fails if the value equals `none` */ match *opt { Some(x) => x, None => fail reason } } pure fn map(opt: &Option, f: fn(x: &T) -> U) -> Option { //! Maps a `some` value by reference from one type to another match *opt { Some(ref x) => Some(f(x)), None => None } } pure fn map_consume(+opt: Option, f: fn(+v: T) -> U) -> Option { /*! * As `map`, but consumes the option and gives `f` ownership to avoid * copying. */ if opt.is_some() { Some(f(option::unwrap(move opt))) } else { None } } pure fn chain(opt: &Option, f: fn(T) -> Option) -> Option { /*! * Update an optional value by optionally running its content through a * function that returns an option. */ match *opt { Some(x) => f(x), None => None } } pure fn chain_ref(opt: &Option, f: fn(x: &T) -> Option) -> Option { /*! * Update an optional value by optionally running its content by reference * through a function that returns an option. */ match *opt { Some(ref x) => f(x), None => None } } pure fn or(+opta: Option, +optb: Option) -> Option { /*! * Returns the leftmost some() value, or none if both are none. */ match opta { Some(_) => move opta, _ => move optb } } #[inline(always)] pure fn while_some(+x: Option, blk: fn(+v: T) -> Option) { //! Applies a function zero or more times until the result is none. let mut opt <- x; while opt.is_some() { opt = blk(unwrap(move opt)); } } pure fn is_none(opt: &Option) -> bool { //! Returns true if the option equals `none` match *opt { None => true, Some(_) => false } } pure fn is_some(opt: &Option) -> bool { //! Returns true if the option contains some value !is_none(opt) } pure fn get_default(opt: &Option, +def: T) -> T { //! Returns the contained value or a default match *opt { Some(x) => x, None => def } } pure fn map_default(opt: &Option, +def: U, f: fn(x: &T) -> U) -> U { //! Applies a function to the contained value or returns a default match *opt { None => move def, Some(ref t) => f(t) } } // This should change to by-copy mode; use iter_ref below for by reference pure fn iter(opt: &Option, f: fn(T)) { //! Performs an operation on the contained value or does nothing match *opt { None => (), Some(t) => f(t) } } pure fn iter_ref(opt: &Option, f: fn(x: &T)) { //! Performs an operation on the contained value by reference match *opt { None => (), Some(ref t) => f(t) } } // tjc: shouldn't this be - instead of +? // then could get rid of some superfluous moves #[inline(always)] pure fn unwrap(+opt: Option) -> T { /*! * Moves a value out of an option type and returns it. * * Useful primarily for getting strings, vectors and unique pointers out * of option types without copying them. */ match move opt { Some(move x) => move x, None => fail ~"option::unwrap none" } } /// The ubiquitous option dance. #[inline(always)] fn swap_unwrap(opt: &mut Option) -> T { if opt.is_none() { fail ~"option::swap_unwrap none" } unwrap(util::replace(opt, None)) } pure fn unwrap_expect(+opt: Option, reason: &str) -> T { //! As unwrap, but with a specified failure message. if opt.is_none() { fail reason.to_unique(); } unwrap(move opt) } // Some of these should change to be &Option, some should not. See below. impl Option { /** * Update an optional value by optionally running its content through a * function that returns an option. */ pure fn chain(f: fn(T) -> Option) -> Option { chain(&self, f) } /// Performs an operation on the contained value or does nothing pure fn iter(f: fn(T)) { iter(&self, f) } /// Returns true if the option equals `none` pure fn is_none() -> bool { is_none(&self) } /// Returns true if the option contains some value pure fn is_some() -> bool { is_some(&self) } } impl &Option { /** * Update an optional value by optionally running its content by reference * through a function that returns an option. */ pure fn chain_ref(f: fn(x: &T) -> Option) -> Option { chain_ref(self, f) } /// Applies a function to the contained value or returns a default pure fn map_default(+def: U, f: fn(x: &T) -> U) -> U { map_default(self, move def, f) } /// Performs an operation on the contained value by reference pure fn iter_ref(f: fn(x: &T)) { iter_ref(self, f) } /// Maps a `some` value from one type to another by reference pure fn map(f: fn(x: &T) -> U) -> Option { map(self, f) } /// Gets an immutable reference to the value inside a `some`. pure fn get_ref() -> &self/T { get_ref(self) } } impl Option { /** * Gets the value out of an option * * # Failure * * Fails if the value equals `none` */ pure fn get() -> T { get(&self) } pure fn get_default(+def: T) -> T { get_default(&self, def) } /** * Gets the value out of an option, printing a specified message on * failure * * # Failure * * Fails if the value equals `none` */ pure fn expect(+reason: ~str) -> T { expect(&self, reason) } /// Applies a function zero or more times until the result is none. pure fn while_some(blk: fn(+v: T) -> Option) { while_some(self, blk) } } impl Option : Eq { pure fn eq(other: &Option) -> bool { match self { None => { match (*other) { None => true, Some(_) => false } } Some(self_contents) => { match (*other) { None => false, Some(ref other_contents) => self_contents.eq(other_contents) } } } } pure fn ne(other: &Option) -> bool { !self.eq(other) } } #[test] fn test_unwrap_ptr() { let x = ~0; let addr_x = ptr::addr_of(*x); let opt = Some(x); let y = unwrap(opt); let addr_y = ptr::addr_of(*y); assert addr_x == addr_y; } #[test] fn test_unwrap_str() { let x = ~"test"; let addr_x = str::as_buf(x, |buf, _len| ptr::addr_of(buf)); let opt = Some(x); let y = unwrap(opt); let addr_y = str::as_buf(y, |buf, _len| ptr::addr_of(buf)); assert addr_x == addr_y; } #[test] fn test_unwrap_resource() { struct R { i: @mut int, drop { *(self.i) += 1; } } fn R(i: @mut int) -> R { R { i: i } } let i = @mut 0; { let x = R(i); let opt = Some(x); let _y = unwrap(opt); } assert *i == 1; } #[test] fn test_option_dance() { let x = Some(()); let mut y = Some(5); let mut y2 = 0; do x.iter |_x| { y2 = swap_unwrap(&mut y); } assert y2 == 5; assert y.is_none(); } #[test] #[should_fail] #[ignore(cfg(windows))] fn test_option_too_much_dance() { let mut y = Some(util::NonCopyable()); let _y2 = swap_unwrap(&mut y); let _y3 = swap_unwrap(&mut y); } #[test] fn test_option_while_some() { let mut i = 0; do Some(10).while_some |j| { i += 1; if (j > 0) { Some(j-1) } else { None } } assert i == 11; } // Local Variables: // mode: rust; // fill-column: 78; // indent-tabs-mode: nil // c-basic-offset: 4 // buffer-file-coding-system: utf-8-unix // End: