188

&String引数としてa を取るいくつかの Rust コードを書きました。

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
}

ただし、このようにするのは得策ではないというフィードバックをいくつか受け取りました。なぜだめですか?

4

2 に答える 2

245

TL;DR: 代わりに&str,&[T]または&Tを使用して、より一般的なコードを使用できます。


  1. Stringaまたは aを使用する主な理由の 1 つはVec、容量を増減できるからです。ただし、不変の参照を受け入れると、これらの興味深いメソッドをVecまたはで使用することはできませんString

  2. またはを受け入れるには&String、関数を呼び出す前に引数をヒープに割り当てる必要があります。a を受け入れると(プログラム データに保存された) 文字列リテラルが許可され、orを受け入れるとスタック割り当ての配列または変数が許可されます。不必要な割り当ては、パフォーマンスの損失です。これは通常、テストまたはメソッドでこれらのメソッドを呼び出そうとするとすぐに明らかになります。&Vec&Box&str&[T]&Tmain

    awesome_greeting(&String::from("Anna"));
    
    total_price(&vec![42, 13, 1337])
    
    is_even(&Box::new(42))
    
  3. パフォーマンスに関するもう 1 つの考慮事項は&String、を取得するために を参照解除し、最終的に を取得するために 2 番目の参照解除を実行する必要があるため、不要な間接参照層を導入すること&Vecです。&Box&StringString&str

代わりに、文字列スライス( &str)、スライス( &[T])、または単なる参照 ( )を受け入れる必要があります&T&String&Vec<T>またはは、それぞれ ( deref coercionを介して) 、またはに&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"Stringtotal_price&[1, 2, 3]Vec


Stringまたはから項目を追加または削除したい場合は、変更可能な参照(または)Vec<T>を取得できます。&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();
    }
}
于 2016-10-12T18:50:50.360 に答える
38

Shepmaster の answerに加えて、 a &str(および同様になど)を受け入れるもう 1 つの理由は、and以外&[T]のすべてのタイプが満たされるためです。最も顕著な例の 1 つは です。これにより、所有するデータと借用したデータのどちらを扱うかについて、非常に柔軟に対応できます。 String&strDeref<Target = str>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使用できます。CowString

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);

Accept&strにより、関数の呼び出しがより均一で便利になり、「最も簡単な」方法が最も効率的になります。これらの例は、Cow<[T]>etcでも機能します。

于 2018-09-12T09:01:43.640 に答える