From d154b9daef4201915428e12018b13183045d0658 Mon Sep 17 00:00:00 2001 From: Alexandre Oliveira Date: Fri, 22 Jan 2016 22:46:25 -0200 Subject: [PATCH 1/6] Add str.substr() and str.substr_until() methods str.substr(start_index: usize) returns a substring of str starting from the specified point (Hello.substr(1) = ello) str.substr_until(start_index: usize, length: usize) returns a substring of str starting from the specified point, with the specified length (Hello.substr_until(0, 2) = He) --- src/libcollections/lib.rs | 1 + src/libcollections/str.rs | 43 +++++++++++++++++++++++++++++++++++++++ src/libcore/str/mod.rs | 18 ++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/src/libcollections/lib.rs b/src/libcollections/lib.rs index 6077a4c01045b..fbfab1b452a37 100644 --- a/src/libcollections/lib.rs +++ b/src/libcollections/lib.rs @@ -55,6 +55,7 @@ #![feature(unicode)] #![feature(unique)] #![feature(unsafe_no_drop_flag)] +#![feature(str_substr_method)] #![cfg_attr(test, feature(clone_from_slice, rand, test))] #![no_std] diff --git a/src/libcollections/str.rs b/src/libcollections/str.rs index 9ce1a111cf83a..12904e0cc93f7 100644 --- a/src/libcollections/str.rs +++ b/src/libcollections/str.rs @@ -1683,6 +1683,49 @@ impl str { pub fn parse(&self) -> Result { core_str::StrExt::parse(self) } + + /// Returns a string slice extracted from the starting index to + /// the end of the string. + /// + /// Returns [`None`] if length is zero, or if string is empty. + /// # Example + /// + /// Basic usage + /// + /// ``` + /// #![feature(str_substr_method)] + /// + /// let string: String = String::from("Hello, world!"); + /// + /// assert_eq!(Some("world!"), string.substr(7)); + /// ``` + #[unstable(feature="str_substr_method", + issue = "0")] + #[inline] + pub fn substr(&self, start_index: usize) -> Option<&str> { + core_str::StrExt::substr(self, start_index) + } + + /// Returns a string slice extracted from between the starting index + /// to the specified length. + /// + /// # Example + /// + /// Basic usage + /// + /// ``` + /// #![feature(str_substr_method)] + /// + /// let string: String = String::from("Hello, world!"); + /// + /// assert_eq!(Some("wo"), string.substr(7, 2)); + /// ``` + #[unstable(feature="str_substr_method", + issue = "0")] + #[inline] + pub fn substr_until(&self, start_index: usize, length: usize) -> Option<&str> { + core_str::StrExt::substr_until(self, start_index, length) + } /// Replaces all matches of a pattern with another string. /// diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 3892455395f76..1af00667aa004 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -1589,6 +1589,12 @@ pub trait StrExt { fn is_empty(&self) -> bool; #[stable(feature = "core", since = "1.6.0")] fn parse(&self) -> Result; + #[unstable(feature="str_substr_method", + issue = "0")] + fn substr_until(&self, start_index: usize, length: usize) -> Option<&str>; + #[unstable(feature="str_substr_method", + issue = "0")] + fn substr(&self, start_index: usize) -> Option<&str>; } #[inline(never)] @@ -1905,6 +1911,18 @@ impl StrExt for str { #[inline] fn parse(&self) -> Result { FromStr::from_str(self) } + + #[inline] + fn substr_until(&self, start_index: usize, length: usize) -> Option<&str> { + if length == 0 || self.is_empty() { return None; } + if start_index == 0 && length == self.len() { return Some(self); } + Some(&self[start_index .. start_index + length]) + } + + #[inline] + fn substr(&self, start_index: usize) -> Option<&str> { + self.substr_until(start_index, self.len() - start_index) + } } #[stable(feature = "rust1", since = "1.0.0")] From 0e55e83b190b442961328acb19aba3ed32ee9aaf Mon Sep 17 00:00:00 2001 From: Alexandre Oliveira Date: Sat, 23 Jan 2016 21:13:45 -0200 Subject: [PATCH 2/6] Move RangeArgument to core::ops --- src/libcore/ops.rs | 57 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) 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`. /// From b428108431fe1c9db949d8e43f8b72c11715a63e Mon Sep 17 00:00:00 2001 From: Alexandre Oliveira Date: Sat, 23 Jan 2016 22:31:37 -0200 Subject: [PATCH 3/6] Updated method to use RangeArgument --- src/libcollections/lib.rs | 3 ++- src/libcollections/str.rs | 38 +++++++++++--------------------------- src/libcore/str/mod.rs | 32 ++++++++++++++++---------------- 3 files changed, 29 insertions(+), 44 deletions(-) diff --git a/src/libcollections/lib.rs b/src/libcollections/lib.rs index fbfab1b452a37..8645b01472c3f 100644 --- a/src/libcollections/lib.rs +++ b/src/libcollections/lib.rs @@ -55,7 +55,8 @@ #![feature(unicode)] #![feature(unique)] #![feature(unsafe_no_drop_flag)] -#![feature(str_substr_method)] +#![feature(collections_range)] +#![feature(str_substr)] #![cfg_attr(test, feature(clone_from_slice, rand, test))] #![no_std] diff --git a/src/libcollections/str.rs b/src/libcollections/str.rs index 12904e0cc93f7..a5bec5698368b 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; @@ -1685,7 +1686,7 @@ impl str { } /// Returns a string slice extracted from the starting index to - /// the end of the string. + /// the specified length. /// /// Returns [`None`] if length is zero, or if string is empty. /// # Example @@ -1697,34 +1698,17 @@ impl str { /// /// let string: String = String::from("Hello, world!"); /// - /// assert_eq!(Some("world!"), string.substr(7)); + /// 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_method", - issue = "0")] + #[unstable(feature="str_substr", + issue = "31140")] #[inline] - pub fn substr(&self, start_index: usize) -> Option<&str> { - core_str::StrExt::substr(self, start_index) - } - - /// Returns a string slice extracted from between the starting index - /// to the specified length. - /// - /// # Example - /// - /// Basic usage - /// - /// ``` - /// #![feature(str_substr_method)] - /// - /// let string: String = String::from("Hello, world!"); - /// - /// assert_eq!(Some("wo"), string.substr(7, 2)); - /// ``` - #[unstable(feature="str_substr_method", - issue = "0")] - #[inline] - pub fn substr_until(&self, start_index: usize, length: usize) -> Option<&str> { - core_str::StrExt::substr_until(self, start_index, length) + 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/libcore/str/mod.rs b/src/libcore/str/mod.rs index 1af00667aa004..00d3ed39eac64 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,12 +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_method", - issue = "0")] - fn substr_until(&self, start_index: usize, length: usize) -> Option<&str>; - #[unstable(feature="str_substr_method", - issue = "0")] - fn substr(&self, start_index: usize) -> Option<&str>; + #[unstable(feature="str_substr", + issue = "31140")] + fn substr(&self, range: R) -> Option<&str> + where R: RangeArgument; } #[inline(never)] @@ -1913,15 +1911,17 @@ impl StrExt for str { fn parse(&self) -> Result { FromStr::from_str(self) } #[inline] - fn substr_until(&self, start_index: usize, length: usize) -> Option<&str> { - if length == 0 || self.is_empty() { return None; } - if start_index == 0 && length == self.len() { return Some(self); } - Some(&self[start_index .. start_index + length]) - } - - #[inline] - fn substr(&self, start_index: usize) -> Option<&str> { - self.substr_until(start_index, self.len() - start_index) + 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 self.is_char_boundary(start) && + self.is_char_boundary(end) { return Some(&self[start .. end]); } + + None } } From e68ec146daf9959e58d2ce84e1d9cdf3c43c6d2c Mon Sep 17 00:00:00 2001 From: Alexandre Oliveira Date: Sat, 23 Jan 2016 22:32:05 -0200 Subject: [PATCH 4/6] Add substr method tests --- src/libcollectionstest/lib.rs | 1 + src/libcollectionstest/string.rs | 9 +++++++++ 2 files changed, 10 insertions(+) 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(|| { From 1bb53c7ca9d9f11f3ef4c2499c631bddc1b064a0 Mon Sep 17 00:00:00 2001 From: Alexandre Oliveira Date: Sun, 24 Jan 2016 13:42:32 -0200 Subject: [PATCH 5/6] Update references to RangeArgument --- src/libcollections/range.rs | 60 --------------------------------- src/libcollections/string.rs | 3 +- src/libcollections/vec.rs | 4 +-- src/libcollections/vec_deque.rs | 4 +-- 4 files changed, 3 insertions(+), 68 deletions(-) delete mode 100644 src/libcollections/range.rs 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/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")] From 74ed956bfb158e4d01b02dff243e71e3ec894dcc Mon Sep 17 00:00:00 2001 From: Alexandre Oliveira Date: Tue, 26 Jan 2016 18:48:09 -0200 Subject: [PATCH 6/6] Add self <= end condition Also updated documentation of the method --- src/libcollections/str.rs | 8 +++++++- src/libcore/str/mod.rs | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/libcollections/str.rs b/src/libcollections/str.rs index a5bec5698368b..b1e100ed249ff 100644 --- a/src/libcollections/str.rs +++ b/src/libcollections/str.rs @@ -1688,7 +1688,13 @@ impl str { /// Returns a string slice extracted from the starting index to /// the specified length. /// - /// Returns [`None`] if length is zero, or if string is empty. + /// `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 diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 00d3ed39eac64..4ee7d8e7560cc 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -1918,7 +1918,8 @@ impl StrExt for str { let start = *range.start().unwrap_or(&0); let end = *range.end().unwrap_or(&len); - if self.is_char_boundary(start) && + if start <= end && + self.is_char_boundary(start) && self.is_char_boundary(end) { return Some(&self[start .. end]); } None