Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add str.substr() and str.substr_until() methods #31140

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/libcollections/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
60 changes: 0 additions & 60 deletions src/libcollections/range.rs

This file was deleted.

33 changes: 33 additions & 0 deletions src/libcollections/str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1683,6 +1684,38 @@ impl str {
pub fn parse<F: FromStr>(&self) -> Result<F, F::Err> {
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doc needs update for change of functionality. You could compare it with usual slicing (&s[a..b] style).

///
/// 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<R>(&self, range: R) -> Option<&str>
where R: RangeArgument<usize>
{
core_str::StrExt::substr(self, range)
}

/// Replaces all matches of a pattern with another string.
///
Expand Down
3 changes: 1 addition & 2 deletions src/libcollections/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
4 changes: 1 addition & 3 deletions src/libcollections/vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,14 @@ 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;

#[allow(deprecated)]
use borrow::{Cow, IntoCow};

use super::range::RangeArgument;

/// A growable list type, written `Vec<T>` but pronounced 'vector.'
///
/// # Examples
Expand Down
4 changes: 1 addition & 3 deletions src/libcollections/vec_deque.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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")]
Expand Down
1 change: 1 addition & 0 deletions src/libcollectionstest/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#![feature(unboxed_closures)]
#![feature(unicode)]
#![feature(vec_push_all)]
#![feature(str_substr)]

#[macro_use] extern crate log;

Expand Down
9 changes: 9 additions & 0 deletions src/libcollectionstest/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(|| {
Expand Down
57 changes: 57 additions & 0 deletions src/libcore/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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'.
Expand Down Expand Up @@ -1530,6 +1531,62 @@ impl<Idx: fmt::Debug> fmt::Debug for RangeTo<Idx> {
}
}

/// **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<T> {
/// 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<T> RangeArgument<T> for RangeFull {}

#[unstable(feature = "collections_range",
reason = "waiting for dust to settle on inclusive ranges",
issue = "30877")]
impl<T> RangeArgument<T> for RangeFrom<T> {
fn start(&self) -> Option<&T> {
Some(&self.start)
}
}

#[unstable(feature = "collections_range",
reason = "waiting for dust to settle on inclusive ranges",
issue = "30877")]
impl<T> RangeArgument<T> for RangeTo<T> {
fn end(&self) -> Option<&T> {
Some(&self.end)
}
}

#[unstable(feature = "collections_range",
reason = "waiting for dust to settle on inclusive ranges",
issue = "30877")]
impl<T> RangeArgument<T> for Range<T> {
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`.
///
Expand Down
21 changes: 20 additions & 1 deletion src/libcore/str/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -1589,6 +1589,10 @@ pub trait StrExt {
fn is_empty(&self) -> bool;
#[stable(feature = "core", since = "1.6.0")]
fn parse<T: FromStr>(&self) -> Result<T, T::Err>;
#[unstable(feature="str_substr",
issue = "31140")]
fn substr<R>(&self, range: R) -> Option<&str>
where R: RangeArgument<usize>;
}

#[inline(never)]
Expand Down Expand Up @@ -1905,6 +1909,21 @@ impl StrExt for str {

#[inline]
fn parse<T: FromStr>(&self) -> Result<T, T::Err> { FromStr::from_str(self) }

#[inline]
fn substr<R>(&self, range: R) -> Option<&str>
where R: RangeArgument<usize>
{
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]); }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs the condition start <= end too, to be correct (and never panic).


None
}
}

#[stable(feature = "rust1", since = "1.0.0")]
Expand Down