Rust では文字列の最初の文字を大文字にするのはなぜこんなに複雑なのでしょうか? 質問する

Rust では文字列の最初の文字を大文字にするのはなぜこんなに複雑なのでしょうか? 質問する

の最初の文字を大文字にしたいです&str。これは単純な問題なので、簡単な解決法を望みます。直感的に次のようにすればよいと思います。

let mut s = "foobar";
s[0] = s[0].to_uppercase();

しかし、&strはこのようにインデックス付けできません。私ができた唯一の方法は、過度に複雑すぎるようです。 を&strイテレータに変換し、イテレータをベクトルに変換し、ベクトルの最初の項目を大文字にします。これによりイテレータが作成され、これにインデックスを付けて を作成し、Optionこれを展開して大文字の最初の文字を取得します。次に、ベクトルをイテレータに変換し、これを に変換しString、これを に変換します&str

let s1 = "foobar";
let mut v: Vec<char> = s1.chars().collect();
v[0] = v[0].to_uppercase().nth(0).unwrap();
let s2: String = v.into_iter().collect();
let s3 = &s2;

これより簡単な方法はありますか? もしあるなら、それは何ですか? そうでないなら、なぜ Rust はこのように設計されているのですか?

同様の質問

ベストアンサー1

なぜこんなに複雑なのでしょうか?

1行ずつ分解してみましょう

let s1 = "foobar";

エンコードされたリテラル文字列を作成しましたUTF-8UTF-8では1,114,112のコードポイントユニコード主に日本語で入力する地域から来た人にとっては、かなりコンパクトな方法でアスキー1963年に制定された標準規格です。UTF-8は可変長エンコードは、単一のコードポイントが1~4バイトを取る短いエンコードはASCII用に予約されていますが、多くの漢字はUTF-8では3バイトを占める

let mut v: Vec<char> = s1.chars().collect();

これはアクターのベクトルを作成しますchar。文字は32ビットの数値で、コードポイントに直接マップされます。ASCIIのみのテキストから始めた場合、メモリ要件は4倍になります。アストラル界であれば、おそらくそれ以上は使用していないでしょう。

v[0] = v[0].to_uppercase().nth(0).unwrap();

これは最初のコードポイントを取得し、大文字の変種に変換するよう要求します。残念ながら、英語を話す環境で育った私たちにとっては、「小さな文字」と「大きな文字」の単純な一対一のマッピングは必ずしもそうではない補足:大文字と小文字を区別します昔は、ある箱の手紙が他の箱の上にあったからです

このコードは、コードポイントに対応する大文字の変種がない場合にパニックになります。実際、そのような変種が存在するかどうかはわかりません。また、コードポイントにドイツ語の のように複数の文字を含む大文字の変種がある場合、意味的に失敗する可能性があります。ß実際の世界では ß が大文字になることはないことに注意してください。これは私が常に思い出して検索できる例です。実際、2017-06-29の時点で、ドイツ語のスペルの公式ルールが更新され、両方「ẞ」と「SS」は有効な大文字表記です

let s2: String = v.into_iter().collect();

ここでは、文字を UTF-8 に戻し、実行時にメモリを占有しないように元の変数が定数メモリに格納されていたため、文字を格納するための新しい割り当てが必要になります。

let s3 = &s2;

そして今、私たちはそれへの参照を取りますString

それは単純な問題です

残念ながら、これは真実ではありません。おそらく私たちは世界をエスペラント?

すでに Unicode を適切に処理していると思われますchar::to_uppercase

はい、そう願っています。残念ながら、Unicodeではすべてのケースに対応できるわけではありません。指摘してくれてありがとうトルコ語 I、上部() と小文字の ()バージョンにはドットが付いています。つまり、1つ文字の大文字の正しい表記iは、ロケールソーステキストも同様です。

すべてのデータ型の変換が必要なのはなぜですか?

正確性とパフォーマンスを気にする場合、操作するデータ型が重要になります。Acharは 32 ビットで、文字列は UTF-8 でエンコードされています。これらは異なるものです。

インデックス作成により、マルチバイトのUnicode文字が返される可能性がある

ここでは用語が一致していない可能性があります。char マルチバイト Unicode 文字。

スライスバイト単位で処理すれば文字列は可能ですが、文字境界にない場合は標準ライブラリがパニックを起こします。

文字列をインデックスして文字を取得する方法が実装されなかった理由の1つは、多くの人が文字列をASCII文字の配列として誤用しているためです。文字列をインデックスして文字を取得する方法は、セット文字は決して効率的ではありません。1 ~ 4 バイトを、同じく 1 ~ 4 バイトの値に置き換える必要があり、文字列の残りの部分がかなり頻繁に変化することになります。

to_uppercase大文字を返す可能性がある

上で述べたように、ßは大文字で書くと、2文字

ソリューション

参照trentclの回答ASCII 文字のみを大文字にします。

オリジナル

コードを書くとしたら、次のようになります。

fn some_kind_of_uppercase_first_letter(s: &str) -> String {
    let mut c = s.chars();
    match c.next() {
        None => String::new(),
        Some(f) => f.to_uppercase().chain(c).collect(),
    }
}

fn main() {
    println!("{}", some_kind_of_uppercase_first_letter("joe"));
    println!("{}", some_kind_of_uppercase_first_letter("jill"));
    println!("{}", some_kind_of_uppercase_first_letter("von Hagen"));
    println!("{}", some_kind_of_uppercase_first_letter("ß"));
}

でも、私はおそらく大文字またはユニコードcrates.io で、私より賢い人に任せましょう。

改善された

「私より賢い人」といえば、ヴィードラック氏は指摘する最初の大文字のコードポイントにアクセスした後、イテレータをスライスに戻す方がおそらく効率的です。これにより、memcpy残りのバイトの が可能になります。

fn some_kind_of_uppercase_first_letter(s: &str) -> String {
    let mut c = s.chars();
    match c.next() {
        None => String::new(),
        Some(f) => f.to_uppercase().collect::<String>() + c.as_str(),
    }
}

おすすめ記事