You can use char_indices() then slice the string according to the positions the iterator gives you:
let mut iter = s.char_indices();
let (start, _) = iter.nth(10).unwrap();
let (end, _) = iter.nth(5).unwrap();
let slice = &s[start..end];
However, note that as mentioned in the documentation of chars():
Answer from Chayim Friedman on Stack OverflowIt’s important to remember that
charrepresents a Unicode Scalar Value, and might not match your idea of what a ‘character’ is. Iteration over grapheme clusters may be what you actually want. This functionality is not provided by Rust’s standard library, check crates.io instead.
Videos
You can use char_indices() then slice the string according to the positions the iterator gives you:
let mut iter = s.char_indices();
let (start, _) = iter.nth(10).unwrap();
let (end, _) = iter.nth(5).unwrap();
let slice = &s[start..end];
However, note that as mentioned in the documentation of chars():
It’s important to remember that
charrepresents a Unicode Scalar Value, and might not match your idea of what a ‘character’ is. Iteration over grapheme clusters may be what you actually want. This functionality is not provided by Rust’s standard library, check crates.io instead.
@ChayimFriedman's answer is of course correct, I just wanted to contribute a more telling example:
fn print_string(s: &str) {
println!("String: {}", s);
}
fn main() {
let s: String = "".to_string();
let mut iter = s.char_indices();
// Retrieve the position of the char at pos 1
let (start, _) = iter.nth(1).unwrap();
// Now the next char will be at position `2`. Which would be
// equivalent of querying `.next()` or `.nth(0)`.
// So if we query for `nth(2)` we query 3 characters; meaning
// the position of character 4.
let (end, _) = iter.nth(2).unwrap();
// Gives you a &str, which is exactly what you want.
// A reference to a substring, zero allocations, zero overhead.
let substring = &s[start..end];
print_string(&s);
print_string(substring);
}
String:
String:
I've done it with smileys because smileys are definitely multi-byte unicode characters.
As @ChayimFriedman already noted, the reason why we have to iterate through the char_indices is because unicode characters are variably sized. They can be anywhere from 1 to 8 bytes long, so the only way to find out where the character boundaries are is to actually read the string up to the character we desire.
For characters, you can use s.chars().skip(pos).take(len):
fn main() {
let s = "Hello, world!";
let ss: String = s.chars().skip(7).take(5).collect();
println!("{}", ss);
}
Beware of the definition of Unicode characters though.
For bytes, you can use the slice syntax:
fn main() {
let s = b"Hello, world!";
let ss = &s[7..12];
println!("{:?}", ss);
}
You can use the as_str method on the Chars iterator to get back a &str slice after you have stepped on the iterator. So to skip the first start chars, you can call
let s = "Some text to slice into";
let mut iter = s.chars();
iter.by_ref().nth(start); // eat up start values
let slice = iter.as_str(); // get back a slice of the rest of the iterator
Now if you also want to limit the length, you first need to figure out the byte-position of the length character:
let end_pos = slice.char_indices().nth(length).map(|(n, _)| n).unwrap_or(0);
let substr = &slice[..end_pos];
This might feel a little roundabout, but Rust is not hiding anything from you that might take up CPU cycles. That said, I wonder why there's no crate yet that offers a substr method.
Your code example is not very complete. The part that actually causes the error can't be seen in your example.
I guess that your code looks something like this:
pub fn function1(s: String) -> i32 {
let index: &i32 = &1;
let substring = (&s[index..]).to_string();
let counter = function1(substring);
10
}
error[E0277]: the type `String` cannot be indexed by `RangeFrom<&i32>`
--> src/main.rs:3:23
|
3 | let substring = (&s[index..]).to_string();
| ^^^^^^^^^^ `String` cannot be indexed by `RangeFrom<&i32>`
|
= help: the trait `Index<RangeFrom<&i32>>` is not implemented for `String`
Problems
indexmust be ausize, but it is an&i32. This is the main error that you see.- You cannot slice a string directly, you need to convert from char-based indices to byte-based indices first. This can be done by iterating through
char_indices().
Here is a rough sketch of how this might look like:
pub fn function1(s: String) -> i32 {
println!("s: {}", s);
let index: &i32 = &1;
// Try to convert the index to a byte position
let substring = match s.char_indices().nth(*index as usize) {
// If a position with the given index was found in the string, create a substring
Some((pos, _)) => (&s[pos..]).to_string(),
// Else, create an empty string
None => "".to_string(),
};
// Break if the substring is empty, otherwise we would have an infinite recursion
if substring.is_empty() {
return 0;
}
let counter = function1(substring);
counter + 1
}
fn main() {
let input_str = "".to_string();
let result = function1(input_str);
println!("Result: {}", result);
}
s:
s:
s:
s:
Result: 3
Slicing vs copying
With every iteration of your function, you are creating a new copy of the string. This is quite slow, and I don't see a reason why this would be necessary in your case.
What you really want is a slice of the input string. This doesn't copy any data, it simply references a part of the original string.
To achieve that, you would have to change your parameter type from String to &str. There is no reason your function would need to take ownership. Even if you want to take ownership, then to_string() would do so, as it creates a copy of the data. So there really is no reason to use String as the parameter type.
pub fn function1(s: &str) -> i32 {
println!("s: {}", s);
let index: &i32 = &1;
// Try to convert the index to a byte position
let substring = match s.char_indices().nth(*index as usize) {
// If a position with the given index was found in the string, create a substring slice
Some((pos, _)) => &s[pos..],
// Else, use an empty string
None => "",
};
// Break if the substring is empty, otherwise we would have an infinite recursion
if substring.is_empty() {
return 0;
}
let counter = function1(substring);
counter + 1
}
fn main() {
let input_str = "".to_string();
let result = function1(&input_str);
println!("Result: {}", result);
}
s:
s:
s:
s:
Result: 3
You couldn't indexing a string in rust, because strings are encoded in UTF-8.
You could use the method chars and/or char_indices
As from your given code, I can't figure out what method you should use. Have a look at the rust doc.
For further information:
https://doc.rust-lang.org/std/string/struct.String.html
https://doc.rust-lang.org/std/string/struct.String.html#method.chars
https://doc.rust-lang.org/std/string/struct.String.html#method.char_indices
https://doc.rust-lang.org/std/string/struct.String.html#method.split_whitespace