どのタイミングでどっちを使うのか分からなかったのでメモ。

開発環境

> rustc --version
rustc 1.44.1 (c7087fe00 2020-06-17)

所有権

&str

文字列リテラルは&str。

fn main() {
    let s = "Hello";
    let t = s;
    println!("{}", s);  // Hello
}

sは &str に束縛され、所有権を保持。
tsの文字列のコピーに束縛され、所有権を保持。
sは所有権を保持しているため、println!で値を出力できる。

&str にはCopyトレイトが実装されているため、コピーが可能。
 

String

// コンパイルエラー
fn main() {
    let s = "Hello".to_string();  // &str -> String
    let t = s;
    println!("{}", s);  // error[E0382]: borrow of moved value: `s`
}

sは String に束縛され、所有権を保持。
tsの文字列に束縛され、所有権がsからtに移動。
sは所有権を保持していないため、println!で値を出力できない。

String にはCopyトレイトが実装されていないため、コピーができない。

ちなみに、to_string()to_owned()でもStringに変換可能。

借用と参照

&str

参照と借用のコードのStringを&strに変更。

fn main() {
    let s1 = "hello";

    let len = calculate_length(s1);

    // '{}'の長さは、{}です
    println!("The length of '{}' is {}.", s1, len);  // The length of 'hello' is 5.
}

fn calculate_length(s: &str) -> usize {
    s.len()
}

 

let len = calculate_length(s1);

値をそのまま実引数として渡していて、所有権移動しないのか?と思ったが、

its(&str) contents are borrowed.
(from std::str - Rust)

より、所有権の移動は発生しておらず、借用が行われている。
借用とは、関数の引数に参照を取ることを指す。
文字列リテラル自体が既に &str なので、引数に渡した時点で借用が発生する。

String

参照と借用のコードを見る。

fn main() {
    let s1 = String::from("hello");

    let len = calculate_length(&s1);

    // '{}'の長さは、{}です
    println!("The length of '{}' is {}.", s1, len);  // The length of 'hello' is 5.
}

fn calculate_length(s: &String) -> usize {
    s.len()
}

もし、calculate_lengths1のみを渡すと、コンパイルエラーになる。

// コンパイルエラー
fn main() {
    let s1 = String::from("hello");

    let len = calculate_length(s1);  // ここで関数にs1の所有権を移動させている

    // '{}'の長さは、{}です
    println!("The length of '{}' is {}.", s1, len);  // error[E0382]: borrow of moved value: `s1`
}

fn calculate_length(s: String) -> usize {
    s.len()
}

使い分け

値の追加などしない限りは &str で良いんじゃない?という感想に落ち着いたが、使っていくうちに何か分かるだろう。

参考