diff --git a/src/libcollections/lib.rs b/src/libcollections/lib.rs index 6077a4c01045b..8645b01472c3f 100644 --- a/src/libcollections/lib.rs +++ b/src/libcollections/lib.rs @@ -55,6 +55,8 @@ #![feature(unicode)] #![feature(unique)] #![feature(unsafe_no_drop_flag)] +#![feature(collections_range)] +#![feature(str_substr)] #![cfg_attr(test, feature(clone_from_slice, rand, test))] #![no_std] diff --git a/src/libcollections/range.rs b/src/libcollections/range.rs deleted file mode 100644 index afcd779ddf19f..0000000000000 --- a/src/libcollections/range.rs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2015 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. - -#![unstable(feature = "collections_range", - reason = "waiting for dust to settle on inclusive ranges", - issue = "30877")] - -//! Range syntax. - -use core::option::Option::{self, None, Some}; -use core::ops::{RangeFull, Range, RangeTo, RangeFrom}; - -/// **RangeArgument** is implemented by Rust's built-in range types, produced -/// by range syntax like `..`, `a..`, `..b` or `c..d`. -pub trait RangeArgument { - /// Start index (inclusive) - /// - /// Return start value if present, else `None`. - fn start(&self) -> Option<&T> { - None - } - - /// End index (exclusive) - /// - /// Return end value if present, else `None`. - fn end(&self) -> Option<&T> { - None - } -} - - -impl RangeArgument for RangeFull {} - -impl RangeArgument for RangeFrom { - fn start(&self) -> Option<&T> { - Some(&self.start) - } -} - -impl RangeArgument for RangeTo { - fn end(&self) -> Option<&T> { - Some(&self.end) - } -} - -impl RangeArgument for Range { - fn start(&self) -> Option<&T> { - Some(&self.start) - } - fn end(&self) -> Option<&T> { - Some(&self.end) - } -} diff --git a/src/libcollections/str.rs b/src/libcollections/str.rs index 9ce1a111cf83a..b1e100ed249ff 100644 --- a/src/libcollections/str.rs +++ b/src/libcollections/str.rs @@ -27,6 +27,7 @@ use core::str as core_str; use core::str::pattern::Pattern; use core::str::pattern::{Searcher, ReverseSearcher, DoubleEndedSearcher}; use core::mem; +use core::ops::RangeArgument; use rustc_unicode::str::{UnicodeStr, Utf16Encoder}; use vec_deque::VecDeque; @@ -1683,6 +1684,38 @@ impl str { pub fn parse(&self) -> Result { core_str::StrExt::parse(self) } + + /// Returns a string slice extracted from the starting index to + /// the specified length. + /// + /// `substr` is a safe implementation of the normal slicing method. + /// Instead of throwing, the method will simply return [`None`] if + /// anything invalid occurs (i.e. start and/or end out of character + /// boundaries). + /// + /// [`None`]: option/enum.Option.html#variant.None + /// + /// # Example + /// + /// Basic usage + /// + /// ``` + /// #![feature(str_substr_method)] + /// + /// let string: String = String::from("Hello, world!"); + /// + /// assert_eq!(Some("Hello"), string.substr(..5)); + /// assert_eq!(Some("world!"), string.substr(7..)); + /// assert_eq!(Some("ello"), string.substr(1..5)); + /// ``` + #[unstable(feature="str_substr", + issue = "31140")] + #[inline] + pub fn substr(&self, range: R) -> Option<&str> + where R: RangeArgument + { + core_str::StrExt::substr(self, range) + } /// Replaces all matches of a pattern with another string. /// diff --git a/src/libcollections/string.rs b/src/libcollections/string.rs index d9cbc4488fca7..b8d338ac0efe7 100644 --- a/src/libcollections/string.rs +++ b/src/libcollections/string.rs @@ -59,7 +59,7 @@ use core::fmt; use core::hash; use core::iter::FromIterator; use core::mem; -use core::ops::{self, Add}; +use core::ops::{self, Add, RangeArgument}; use core::ptr; use core::slice; use core::str::pattern::Pattern; @@ -68,7 +68,6 @@ use rustc_unicode::str as unicode_str; #[allow(deprecated)] use borrow::{Cow, IntoCow}; -use range::RangeArgument; use str::{self, FromStr, Utf8Error, Chars}; use vec::Vec; use boxed::Box; diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index a49b7304643cc..453f5aaf0cdf7 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -68,7 +68,7 @@ use core::hash::{self, Hash}; use core::intrinsics::{arith_offset, assume, needs_drop}; use core::iter::FromIterator; use core::mem; -use core::ops::{Index, IndexMut}; +use core::ops::{Index, IndexMut, RangeArgument}; use core::ops; use core::ptr; use core::slice; @@ -76,8 +76,6 @@ use core::slice; #[allow(deprecated)] use borrow::{Cow, IntoCow}; -use super::range::RangeArgument; - /// A growable list type, written `Vec` but pronounced 'vector.' /// /// # Examples diff --git a/src/libcollections/vec_deque.rs b/src/libcollections/vec_deque.rs index 394f7a975989a..845259f23a8b6 100644 --- a/src/libcollections/vec_deque.rs +++ b/src/libcollections/vec_deque.rs @@ -22,7 +22,7 @@ use core::cmp::Ordering; use core::fmt; use core::iter::{repeat, FromIterator}; use core::mem; -use core::ops::{Index, IndexMut}; +use core::ops::{Index, IndexMut, RangeArgument}; use core::ptr; use core::slice; @@ -31,8 +31,6 @@ use core::cmp; use alloc::raw_vec::RawVec; -use super::range::RangeArgument; - const INITIAL_CAPACITY: usize = 7; // 2^3 - 1 const MINIMUM_CAPACITY: usize = 1; // 2 - 1 #[cfg(target_pointer_width = "32")] diff --git a/src/libcollectionstest/lib.rs b/src/libcollectionstest/lib.rs index e57620dfb04eb..44a471982f491 100644 --- a/src/libcollectionstest/lib.rs +++ b/src/libcollectionstest/lib.rs @@ -37,6 +37,7 @@ #![feature(unboxed_closures)] #![feature(unicode)] #![feature(vec_push_all)] +#![feature(str_substr)] #[macro_use] extern crate log; diff --git a/src/libcollectionstest/string.rs b/src/libcollectionstest/string.rs index 89df77d074fdc..f5545425f9bf2 100644 --- a/src/libcollectionstest/string.rs +++ b/src/libcollectionstest/string.rs @@ -372,6 +372,15 @@ fn test_into_boxed_str() { assert_eq!(&*ys, "hello my name is bob"); } +#[test] +fn test_substr() { + let x = String::from("Hello, world!"); + + assert_eq!(Some("Hello"), x.substr(..5)); + assert_eq!(Some("world!"), x.substr(7..)); + assert_eq!(Some("ello"), x.substr(1..5)); +} + #[bench] fn bench_with_capacity(b: &mut Bencher) { b.iter(|| { diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index d1c5b175bb034..45e1e1c4a8317 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -69,6 +69,7 @@ use marker::{Sized, Unsize}; use fmt; +use option::Option::{self, None, Some}; /// The `Drop` trait is used to run some code when a value goes out of scope. /// This is sometimes called a 'destructor'. @@ -1530,6 +1531,62 @@ impl fmt::Debug for RangeTo { } } +/// **RangeArgument** is implemented by Rust's built-in range types, produced +/// by range syntax like `..`, `a..`, `..b` or `c..d`. +#[unstable(feature = "collections_range", + reason = "waiting for dust to settle on inclusive ranges", + issue = "30877")] +pub trait RangeArgument { + /// Start index (inclusive) + /// + /// Return start value if present, else `None`. + fn start(&self) -> Option<&T> { + None + } + + /// End index (exclusive) + /// + /// Return end value if present, else `None`. + fn end(&self) -> Option<&T> { + None + } +} + +#[unstable(feature = "collections_range", + reason = "waiting for dust to settle on inclusive ranges", + issue = "30877")] +impl RangeArgument for RangeFull {} + +#[unstable(feature = "collections_range", + reason = "waiting for dust to settle on inclusive ranges", + issue = "30877")] +impl RangeArgument for RangeFrom { + fn start(&self) -> Option<&T> { + Some(&self.start) + } +} + +#[unstable(feature = "collections_range", + reason = "waiting for dust to settle on inclusive ranges", + issue = "30877")] +impl RangeArgument for RangeTo { + fn end(&self) -> Option<&T> { + Some(&self.end) + } +} + +#[unstable(feature = "collections_range", + reason = "waiting for dust to settle on inclusive ranges", + issue = "30877")] +impl RangeArgument for Range { + fn start(&self) -> Option<&T> { + Some(&self.start) + } + fn end(&self) -> Option<&T> { + Some(&self.end) + } +} + /// The `Deref` trait is used to specify the functionality of dereferencing /// operations, like `*v`. /// diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 3892455395f76..4ee7d8e7560cc 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -27,7 +27,7 @@ use iter::ExactSizeIterator; use iter::{Map, Cloned, Iterator, DoubleEndedIterator}; use marker::Sized; use mem; -use ops::{Fn, FnMut, FnOnce}; +use ops::{Fn, FnMut, FnOnce, RangeArgument}; use option::Option::{self, None, Some}; use raw::{Repr, Slice}; use result::Result::{self, Ok, Err}; @@ -1589,6 +1589,10 @@ pub trait StrExt { fn is_empty(&self) -> bool; #[stable(feature = "core", since = "1.6.0")] fn parse(&self) -> Result; + #[unstable(feature="str_substr", + issue = "31140")] + fn substr(&self, range: R) -> Option<&str> + where R: RangeArgument; } #[inline(never)] @@ -1905,6 +1909,21 @@ impl StrExt for str { #[inline] fn parse(&self) -> Result { FromStr::from_str(self) } + + #[inline] + fn substr(&self, range: R) -> Option<&str> + where R: RangeArgument + { + let len = self.len(); + let start = *range.start().unwrap_or(&0); + let end = *range.end().unwrap_or(&len); + + if start <= end && + self.is_char_boundary(start) && + self.is_char_boundary(end) { return Some(&self[start .. end]); } + + None + } } #[stable(feature = "rust1", since = "1.0.0")]