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
Answer from Finomnis on Stack OverflowVideos
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
I am coming from a world of Python, so pointers and references are kind of a new thing for me. So, I was going through The book. I was on the slices chapter, specifically the String Slices as Parameter part. I just don't get this fn first_word(s: &String) -> &str { is worse than fn first_word(s: &str) -> &str { this. Is it because it is a pointer to a pointer, if I understood things correctly?
You can create a function that accepts both &[String] and &[&str] using the AsRef trait:
Copyfn test<T: AsRef<str>>(inp: &[T]) {
for x in inp { print!("{} ", x.as_ref()) }
println!("");
}
fn main() {
let vref = vec!["Hello", "world!"];
let vown = vec!["May the Force".to_owned(), "be with you.".to_owned()];
test(&vref);
test(&vown);
}
This is actually impossible without either memory allocation or per-element call1.
Going from String to &str is not just viewing the bits in a different light; String and &str have a different memory layout, and thus going from one to the other requires creating a new object. The same applies to Vec and &[]
Therefore, whilst you can go from Vec<T> to &[T], and thus from Vec<String> to &[String], you cannot directly go from Vec<String> to &[&str]. Your choices are:
- either accept
&[String] - allocate a new
Vec<&str>referencing the firstVec, and convert that into a&[&str]
As an example of the allocation:
Copyfn usage(_: &[&str]) {}
fn main() {
let owned = vec![String::new()];
let half_owned: Vec<_> = owned.iter().map(String::as_str).collect();
usage(&half_owned);
}
1 Using generics and the AsRef<str> bound as shown in @aSpex's answer you get a slightly more verbose function declaration with the flexibility you were asking for, but you do have to call .as_ref() in all elements.