Как индексировать строку в ржавчине
Я пытаюсь индексировать строку в Rust, но компилятор выдает ошибку. Мой код (Project Euler problem 4, игровая площадка):
fn is_palindrome(num: u64) -> bool {
let num_string = num.to_string();
let num_length = num_string.len();
for i in 0 .. num_length / 2 {
if num_string[i] != num_string[(num_length - 1) - i] {
return false;
}
}
true
}
Ошибка:
error[E0277]: the trait bound `std::string::String: std::ops::Index<usize>` is not satisfied
--> <anon>:7:12
|
7 | if num_string[i] != num_string[(num_length - 1) - i] {
| ^^^^^^^^^^^^^
|
= note: the type `std::string::String` cannot be indexed by `usize`
Есть ли причина, по которой String
не может индексироваться? Как я могу получить доступ к данным?
Ответы
Ответ 1
Правильный подход к выполнению такого рода вещей в Rust - это не индексирование, а итерация. Основная проблема здесь в том, что строки Rust кодируются в UTF-8, кодировке переменной длины для символов Unicode. Будучи переменным по длине, позиция памяти n-го символа не может быть определена без просмотра строки. Это также означает, что доступ к n-му символу имеет время выполнения O (n)!
В этом специальном случае вы можете перебирать байты, потому что ваша строка, как известно, содержит только символы 0-9 (итерация по символам является более общим решением, но немного менее эффективной).
Вот какой идиоматический код для этого (игровая площадка):
fn is_palindrome(num: u64) -> bool {
let num_string = num.to_string();
let half = num_string.len() / 2;
num_string.bytes().take(half).eq(num_string.bytes().rev().take(half))
}
Мы просматриваем байты в строке как вперед (num_string.bytes().take(half)
), так и назад (num_string.bytes().rev().take(half)
) одновременно; часть .take(half)
должна сократить вдвое объем выполненной работы. Затем мы просто сравниваем один итератор с другим, чтобы обеспечить на каждом шаге, что n-й и n-й последние байты эквивалентны; если они есть, он возвращает true; если нет, false.
Ответ 2
Да, недавно была удалена индексация в строку. Это делается потому, что строки Rust являются внутренними UTF-8, поэтому концепция индексирования сама по себе неоднозначна, и люди склонны ее неправильно использовать: индексирование байтов происходит быстро, но почти всегда неверно - когда ваш текст содержит символы, отличные от ASCII, индексирование байтов может оставьте вас внутри символа, что очень плохо, если вам нужна текстовая обработка, а индексация char не является бесплатной, поскольку UTF-8 является кодировкой переменной длины.
Если вы уверены, что ваши строки содержат только символы ASCII, вы можете использовать Ascii
(используя метод to_ascii()
) или as_bytes()
на &str
, который возвращает байтовый срез:
let num_string = num.to_str().as_slice();
// ...
num_string.as_bytes()[i]
Если вам нужна индексация символов, вы должны использовать метод char_at()
:
num_string.char_at(i)
Ответ 3
Альтернатива другим ответам "Если я хорошо понимаю, что вы просите".
Я оставлю ссылку, потому что мой английский не очень хорош, и я не могу объяснить это правильно.
Если то, что вы ищете, похоже на индекс, вы можете использовать
.chars()
и .nth()
На строке.
.chars()
→ Возвращает итератор по символам среза строки.
.nth()
→ Возвращает n-й элемент итератора, в → Option
Теперь вы можете использовать приведенное выше несколько способов, например:
let s: String = String::from("abc");
//If you are sure
println!("{}", s.chars().nth(x).unwrap());
//or if not
println!("{}", s.chars().nth(x).expect("message"));
Ответ 4
Моя попытка новичка, протестированная на Rust 1.20 в ночное время, работает для меня.
let col = 20; //pos we need to find
let mut i: i32 = 0;
for (id, c) in line.char_indices() {
if *col == id as i32 {
return c;
}
i += 1;
}
//return something or call panic! here