Vec
コード
fn main() {
let mut v: Vec<String> = vec![];
for i in 0..5 {
v.push("h".to_string());
}
println!("&v: {:p}", &v);
println!("&v[0]: {:p}", &v[0]);
println!("&v[1]: {:p}", &v[1]);
println!("&v[2]: {:p}", &v[2]);
}
結果
&v: 0x7ffeebfd20a0
&v[0]: 0x7ff29bc02ce0
&v[1]: 0x7ff29bc02cf8
&v[2]: 0x7ff29bc02d10
アドレス値は16進数で表現されている。
vectorは連続したある程度のサイズのメモリを一度に確保する。
この事からStringは24byteをベースに持ち、ヒープにstringの中身のchar配列を持っててそれ分の用量を食ってる様子が伺える。
ちなみにv[0]
等でStringを参照しているが、これは全て借用で借りているので、v[0]
とかの所有権だけ欲しいとかはできない。どうしても欲しい場合はクローンする必要がある。
Vec
コード
fn main() {
let mut v: Vec<i32> = vec![];
for i in 0..5 {
v.push(i);
}
println!("&v: {:p}", &v);
println!("&v[0]: {:p}", &v[0]);
println!("&v[1]: {:p}", &v[1]);
println!("&v[2]: {:p}", &v[2]);
}
i32は名前の通り32ビット型の整数型である。
このコードを実行すると以下の出力を確認できる。
&v: 0x7ffeefbda0b8
&v[0]: 0x7fd791c02c60
&v[1]: 0x7fd791c02c64
&v[2]: 0x7fd791c02c68
32ビットは4バイト。ちゃんと連続で確保されている。正しい。
Vecのメモリ引っ越しの確認
新規要素をベクタの終端に追加すると、ベクタが現在存在する位置に隣り合って要素を入れるだけの領域がなかった場合に、 メモリの新規確保をして古い要素を新しいスペースにコピーする。
この挙動を実際に確認します。
対象コードはこちら
fn main() {
let mut v: Vec<i32> = vec![];
for i in 0..5 {
v.push(i);
}
println!("&v: {:p}", &v);
println!("&v[0]: {:p}", &v[0]);
println!("&v[1]: {:p}", &v[1]);
println!("&v[2]: {:p}", &v[2]);
v.push(1);
println!("&v: {:p}", &v);
println!("&v[0]: {:p}", &v[0]);
println!("&v[1]: {:p}", &v[1]);
println!("&v[2]: {:p}", &v[2]);
for i in 0..5 {
v.push(i);
}
println!("&v: {:p}", &v);
println!("&v[0]: {:p}", &v[0]);
println!("&v[1]: {:p}", &v[1]);
println!("&v[2]: {:p}", &v[2]);
}
結果
&v: 0x7ffeef230dc8
&v[0]: 0x7fc53dc02c60
&v[1]: 0x7fc53dc02c64
&v[2]: 0x7fc53dc02c68
&v: 0x7ffeef230dc8
&v[0]: 0x7fc53dc02c60
&v[1]: 0x7fc53dc02c64
&v[2]: 0x7fc53dc02c68
&v: 0x7ffeef230dc8
&v[0]: 0x7fc53dc02d70
&v[1]: 0x7fc53dc02d74
&v[2]: 0x7fc53dc02d78
1つだけpushしても余分にとっていた様で、再確保されませんでしたが、追加でいくつかpushしたら再確保されましたね。
vectorのこの仕様のおかげでvector内を探索したり要素を参照するのがとても高速ですが、沢山pushしたり長さを調整すると何度も再確保され、そこにコストがかかっている様子が確認できました。