![](https://doc.rust-lang.org/book/second-edition/]
Apache License Version 2.0
8. Common Collections
Strings
What is String?
Rustが実装している文字列型はstr
のみで,ふつう&str
で利用される.String
はRustの標準ライブラリに実装されていて,ともにUTF-8でエンコードされている.
Creating a New String
Vec
と同じく,new
によって新しいString
を定義できる.
let s = String::new();
はs
という空の文字列を新たに宣言する.また,変数に入れたい文字列が決まっているときは,
let data = "initial contents";
let s = data.to_string();
// これらは結局同じこと
let s = "initial contents".to_string();
このように,to_string
methodによって,String
型の変数を作れる.to_string
はDisplay
を実装している型なら持っている.また,String::fromで文字列リテラルを受け取るとString
にできることはすでに見た.
let s = String::from("initial contents");
Updating a String
Vec
と同じくString
は伸び縮みできるし,その中身自体を変更することもできる.特に,+
演算子によってString
同士をつなげることができる.
Appending to a String with Push
String
にpush_str
methodによって末尾にさらに文字列を追加できる.
fn main() {
let mut s = String::from("foo");
s.push_str("bar");
println!("s is {}", s);
}
shell
s is foobar
さらに,string literalはStringのreferenceだから,(Chap. 4)
fn main() {
let mut s1 = String::from("foo");
let s2 = String::from("bar");
s1.push_str(&s2); // referenceでないとエラー
println!("s1 is {}", s1);
}
としても同じことである.ただし,push_str
はreferenceのみを引数に取る.
また,push
は引数に1文字だけをとる.
fn main() {
let mut s1 = String::from("lo");
s1.push('l'); // 'lo'などとするとエラー
println!("s is {}", s1);
}
Concatenation with the + Operator or the format!
Macro
2つのString
を結合させたいことがある.前節とは別の方法に,+
によるconcatenationがある.
let s1 = String::from("Hello, ");
let s2 = String::from("World!");
let s3 = s1 + &s2; // s1はmoveしてこの時点で消滅する.s2は必ずreference
このようにしてs3
の中身はHello, World!
となる.+
演算子は内部的には
fn add(self, s: &str) -> String{ // 本当はもっと高度なことをしているらしい(Chap. 10)
のような,String
のadd
methodを使っているので,ownershipのmoveによってs1
は消えてしまう.ところで,&s2
の型は&String
で,&str
ではない.add
の引数として&String
を与えると内部でderef coercionという機能が働いて&str
に変換(原文coerce)する.(&s2 => &s2[..] にする.str
とはString
のsliceであった.)
また,add
は引数のself
のownershipをとっていて,s1
は消える.
2つ以上のString
を結合したいときは,let s = s1 + "-" + &s2 + "-" + &s3;
などとする.またより読みやすい記法としてlet s = format!("{}-{}-{}", s1, s2, s3);
としても同じことである.
Indexing into Strings
rustはString
のindexを指定して文字を取り出す機能を実装していない.
Internal Representation
String
はVec<u8>
のラッパである.つまり,UTF-8のコードの数値をVector
に保持し,それを文字列として扱うための糖衣構文である.
let len = String::from("Hola").len();
とすると,len
は4である.これはUTF-8でラテン文字は1Byteで表現されるためで,例えば
let len = String::from("Здравствуйте").len();
とすると,キリル文字は2Bytesで表現されるのでlen
は24となる.ここで
let hello = "Здравствуйте";
let answer = &hello[0]; // 実際はコンパイルできない
とすると,З
のUTF-8表現は208なので,answer
は208
となって,これは大方のプログラマの予期しない結果と思わるので,Rustはこうした行為ができないようにしている.
Bytes and Scalar Values and Grapheme Clusters! Oh my!
Grapheme Clustersとは,我々が単にletters(文字列?)と呼ぶものに最も近いRust内の表現である.
“नमस्ते”というヒンズー語の文字列は,Rust内部では究極的に
“`rust
[224, 164, 168, 224, 164, 174, 224, 164, 184, 224, 165, 141, 224, 164, 164,
224, 165, 135]
という```Vec<u8>```型である.
これは18bytesあって,ヒンズー語の文字としてそれぞれを分けてみると,
```rust
['न', 'म', 'स', '्', 'त', 'े']
<div class="se-preview-section-delimiter"></div>
左から4,6番目はletterでなく,diacritic(発音記号)であって,結局grapheme clustersは
["न", "म", "स्", "ते"]
<div class="se-preview-section-delimiter"></div>
のようになる.このように,プログラマの直感とはかなり異なった方法でRustは文字列を保持していて,String
のインデクシングを直感的に行えるようにするには,計算量が(O(1))では効かなくなる.インデクシングは常に定数計算量(O(1))であるべきと開発者が考えたため,String
はインデクシングを実装していない.
Slicing Strings
String
をbyteごとにスライスすることはできる.
let hello = "Здравствуйте"
let s = &hello[0..4];
s
は最初から4番目までのByteへのrefereceで,&str
型である.キリル文字は2bytes文字だから,s
をプリントするとЗд
となる.
&hello[0..1]
とすると,最初の1byteのみ保持するようになるはずだが,それぞれの文字の境界に両端がないスライシングをしようとすると実行時にPanicを起こす.
Methods for iterating Over Strings
String
のそれぞれの文字にアクセスできる方法は他にもある.
String
に含まれるUTF-8のscalar value(つまり文字)に何かを実行したいときは,chars
methodを使うのが一番良い.“नमस्ते”にchars
を適用すると,もとの文字列を分解して,それぞれの文字をchar
型にしてくれる.
for c in “नमस्ते”.chars() {
println!("{}", c);
}
は
न
म
स
्
त
े
を返す.(発音記号ごとイテレーションしているがいいのか・・・・)
一方でbytes
methodはそれぞれのbyteを返す.
for b in "नमस्ते".bytes() {
println!("{}", b);
}
shell
224
164
// etc
Strings are Not so Simple
このように,Rustでの文字列の扱いはとても複雑なことがわかっただろう.
0 件のコメント:
コメントを投稿