Как индексировать строку в ржавчине

Я пытаюсь индексировать строку в 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