私はいくつかのRust &String
を引数として:
fn awesome_greeting(name: &String) {
println!("Wow, you are awesome, {}!", name);
}
Vec
またはBox
への参照を受け取るコードも記述しました。
fn total_price(prices: &Vec<i32>) -> i32 {
prices.iter().sum()
}
fn is_even(value: &Box<i32>) -> bool {
**value % 2 == 0
}
ただし、このようにするのは良い考えではないというフィードバックを受け取りました。何故なの?
TL; DR:代わりに&str
、&[T]
または&T
を使用して、より一般的なコードを許可できます。
String
またはVec
を使用する主な理由の1つは、容量を増減できることです。ただし、不変の参照を受け入れる場合、Vec
またはString
でこれらの興味深いメソッドを使用することはできません。
&String
、&Vec
または&Box
も受け入れます必須関数を呼び出す前にヒープに割り当てられる引数。 &str
を受け入れると、文字列リテラル(プログラムデータに保存される)が許可され、&[T]
または&T
を受け入れると、スタックに割り当てられた配列または変数が許可されます。不要な割り当てはパフォーマンスの低下です。これは通常、テストまたはmain
メソッドでこれらのメソッドを呼び出そうとするとすぐに公開されます。
awesome_greeting(&String::from("Anna"));
total_price(&vec![42, 13, 1337])
is_even(&Box::new(42))
パフォーマンスに関するもう1つの考慮事項は、&String
、&Vec
、および&Box
は、&String
を逆参照してString
を取得してから実行する必要があるため、不要な間接レイヤーを導入することです&str
で終わる2番目の逆参照。
代わりに、string slice(&str
)、slice(&[T]
)、または単なる参照(&T
)。 &String
、&Vec<T>
、または&Box<T>
は、それぞれ自動的に&str
、&[T]
、または&T
に強制変換されます。
fn awesome_greeting(name: &str) {
println!("Wow, you are awesome, {}!", name);
}
fn total_price(prices: &[i32]) -> i32 {
prices.iter().sum()
}
fn is_even(value: &i32) -> bool {
*value % 2 == 0
}
これで、これらのメソッドをより広範なタイプのセットで呼び出すことができます。たとえば、awesome_greeting
は、文字列リテラル("Anna"
)または割り当てられたString
で呼び出すことができます。 total_price
は、配列への参照で呼び出すことができます(&[1, 2, 3]
)または割り当てられたVec
。
String
またはVec<T>
からアイテムを追加または削除する場合は、mutable reference(&mut String
または&mut Vec<T>
):
fn add_greeting_target(greeting: &mut String) {
greeting.Push_str("world!");
}
fn add_candy_prices(prices: &mut Vec<i32>) {
prices.Push(5);
prices.Push(25);
}
特にスライスの場合、&mut [T]
または&mut str
を受け入れることもできます。これにより、スライス内の特定の値を変更できますが、スライス内のアイテム数を変更することはできません(つまり、文字列に対して非常に制限されています)。
fn reset_first_price(prices: &mut [i32]) {
prices[0] = 0;
}
fn lowercase_first_ascii_character(s: &mut str) {
if let Some(f) = s.get_mut(0..1) {
f.make_ascii_lowercase();
}
}
Shepmaster's answer に加えて、&str
(および同様に&[T]
など)を受け入れる別の理由は、他のすべてのタイプによるものです。他にString
と&str
は、Deref<Target = str>
も満たします。最も注目すべき例の1つはCow<str>
です。これにより、所有データと借用データのどちらを扱うかについて非常に柔軟に対応できます。
あなたが持っている場合:
fn awesome_greeting(name: &String) {
println!("Wow, you are awesome, {}!", name);
}
ただし、Cow<str>
で呼び出す必要があります。これを行う必要があります。
let c: Cow<str> = Cow::from("hello");
// Allocate an owned String from a str reference and then makes a reference to it anyway!
awesome_greeting(&c.to_string());
引数の型を&str
に変更すると、Cow
と同様に、不要な割り当てなしでString
をシームレスに使用できます。
let c: Cow<str> = Cow::from("hello");
// Just pass the same reference along
awesome_greeting(&c);
let c: Cow<str> = Cow::from(String::from("hello"));
// Pass a reference to the owned string that you already have
awesome_greeting(&c);
&str
を受け入れると、関数の呼び出しがより均一で便利になり、「最も簡単な」方法が最も効率的になりました。これらの例は、Cow<[T]>
などでも機能します。