1. ホーム
  2. string

[解決済み] Rustで文字列のインデックスを作成する方法

2023-03-05 12:48:03

質問

Rustで文字列のインデックスを作成しようとしているのですが、コンパイラがエラーを出します。私のコード(プロジェクトオイラー問題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 はインデックスされないのでしょうか?その場合、どのようにデータにアクセスすればよいのでしょうか?

どのように解決するには?

はい、Rustでは文字列へのインデックス付けはできません。その理由は、Rust の文字列は内部的に UTF-8 でエンコードされているため、インデックスの概念自体があいまいで、人々がそれを誤って使用するためです。バイト インデックスは高速ですが、ほとんどの場合正しくありません (テキストに非 ASCII 記号が含まれていると、バイト インデックスでは文字の中に残ってしまうことがあり、テキスト処理が必要な場合は非常にまずい)。一方、文字インデックスは UTF-8 が可変長のエンコーディングなので自由ではなく、必要とするコードポイントを見つけるには、すべての文字を横断しなければなりません。

文字列が ASCII 文字だけを含んでいることが確実な場合は、文字列をインデックスするために as_bytes() メソッドで &str で、バイトのスライスを返し、そのスライスにインデックスを付けます。

let num_string = num.to_string();

// ...

let b: u8 = num_string.as_bytes()[i];
let c: char = b as char;  // if you need to get the character as a unicode code point

コードポイントにインデックスをつける必要がある場合は char() イテレータを使う必要があります。

num_string.chars().nth(i).unwrap()

上で述べたように、この場合、イテレータ全体を i th コード要素までイテレータ全体をトラバースする必要があります。

最後に、テキスト処理の多くのケースで、実際に必要なのは 書記素クラスタ を扱う必要がある。このような場合 ユニコード・セグメンテーション クレートを使えば、書記素クラスタへのインデックスも作成できます。

use unicode_segmentation::UnicodeSegmentation

let string: String = ...;
UnicodeSegmentation::graphemes(&string, true).nth(i).unwrap()

当然ながら、書記素クラスタ索引はコードポイントへの索引と同様に、文字列全体を走査することが要求されます。