[Rust] 引数で文字列の参照を受取る場合に&strと&Stringどちらが良いのか


[前回](https://qiita.com/yagince/items/e7474839246ced595f7a) の投稿で[コメント](https://qiita.com/yagince/items/e7474839246ced595f7a#comment-45f2f3856f128eab37e3)頂いた所で、新たな疑問が浮かんだ。

```rust
fn puts(s: &str) {
    println!("puts:       {:p}", s);
}

let a = "hoge".to_string();
println!("&String:    {:p}", &a);
println!("UTF-8 addr: {:p}", a.as_ptr()); // StringのUTF-8バイト列へのポインタ
println!("&str:       {:p}", a.as_str()); // Stringから&strを得る
puts(&a);                                 // &Stringから&strへ型強制
println!("&str:       {:p}", &a as &str); // &Stringから&strへ型変換
```

前回の投稿で

1. 参照だけでいい場合は、&strを使う
2. 文字列の中身を書き換えたい場合はStringを使うとよい

という理解に至ったけど、
じゃあ上記 `1` の場合 に `&str` と `&String` で受け取るのは、何が違うのか。

# 検証環境
OS: macOS High Sierra 10.13.3
Rust: rustc 1.26.0-nightly (c08480fce 2018-03-23)

# 引数で&strと&Stringを受け取った時のポインタを見てみる
```rust
fn puts_str(s: &str) {
    println!("{:p}", s);
}

fn puts_string(s: &String) {
    println!("{:p}", s.as_ptr());
}

let a = "hoge".to_string();
puts_str(&a);
puts_string(&a);
```
※前回コメント頂いたコードで、`as_ptr` で見ればいい事を知った！

```
0x100a17018
0x100a17018
```

同じですね。

# &strで受け取る場合と&Stringで受け取る場合でベンチマーク取ってみる
## 呼び出し元の変数の型がStringの場合
```rust
extern crate test;

fn print_str(s: &str) {
    println!("{:p}", s);
}

fn print_string(s: &String) {
    println!("{:p}", s);
}

#[cfg(test)]
mod tests {
    use super::*;
    use self::test::Bencher;

    #[bench]
    fn bench_string_to_str(b: &mut Bencher) {
        let s = "hoge".to_string();
        b.iter(|| print_str(&s) );
    }

    #[bench]
    fn bench_string_to_string(b: &mut Bencher) {
        let s = "hoge".to_string();
        b.iter(|| print_string(&s) );
    }
}
```

何度か実行してみた

```
cargo bench
```

```
running 2 tests
test str_vs_string::tests::bench_string_to_str    ... bench:         176 ns/iter (+/- 42)
test str_vs_string::tests::bench_string_to_string ... bench:         176 ns/iter (+/- 37)
```

```
running 2 tests
test str_vs_string::tests::bench_string_to_str    ... bench:         174 ns/iter (+/- 65)
test str_vs_string::tests::bench_string_to_string ... bench:         184 ns/iter (+/- 44)
```

```
running 2 tests
test str_vs_string::tests::bench_string_to_str    ... bench:         183 ns/iter (+/- 24)
test str_vs_string::tests::bench_string_to_string ... bench:         184 ns/iter (+/- 43)
```
> 型強制にはいくつかの規則があるのですが、その中の Deref による型強制 が働いて、&String から &str へ暗黙的に変換されます。

ということだったので、型変換がある分、&strの方が遅いかと思ったけど、
これだけだと、ほぼ変わらなかった。(といっても過言ではない気がする）

## 呼び出し元の変数の型が&strの場合

```rust
extern crate test;

fn print_str(s: &str) {
    println!("{:p}", s);
}

fn print_string(s: &String) {
    println!("{:p}", s);
}

#[cfg(test)]
mod tests {
    use super::*;
    use self::test::Bencher;

    #[bench]
    fn bench_string_to_str(b: &mut Bencher) {
        let s = "hoge".to_string();
        b.iter(|| print_str(&s) );
    }

    #[bench]
    fn bench_string_to_string(b: &mut Bencher) {
        let s = "hoge".to_string();
        b.iter(|| print_string(&s) );
    }

    // 追加
    #[bench]
    fn bench_str_to_str(b: &mut Bencher) {
        let s = "hoge";
        b.iter(|| print_str(&s) );
    }
    // 追加
    #[bench]
    fn bench_str_to_string(b: &mut Bencher) {
        let s = "hoge";
        b.iter(|| print_string(&s.to_string()) );
    }
}
```

```
running 4 tests
test str_vs_string::tests::bench_str_to_str       ... bench:         174 ns/iter (+/- 28)
test str_vs_string::tests::bench_str_to_string    ... bench:         217 ns/iter (+/- 49)
test str_vs_string::tests::bench_string_to_str    ... bench:         176 ns/iter (+/- 10)
test str_vs_string::tests::bench_string_to_string ... bench:         177 ns/iter (+/- 29)
```

```
running 4 tests
test str_vs_string::tests::bench_str_to_str       ... bench:         176 ns/iter (+/- 41)
test str_vs_string::tests::bench_str_to_string    ... bench:         227 ns/iter (+/- 60)
test str_vs_string::tests::bench_string_to_str    ... bench:         175 ns/iter (+/- 55)
test str_vs_string::tests::bench_string_to_string ... bench:         186 ns/iter (+/- 50)

```

```
running 4 tests
test str_vs_string::tests::bench_str_to_str       ... bench:         175 ns/iter (+/- 42)
test str_vs_string::tests::bench_str_to_string    ... bench:         226 ns/iter (+/- 142)
test str_vs_string::tests::bench_string_to_str    ... bench:         185 ns/iter (+/- 53)
test str_vs_string::tests::bench_string_to_string ... bench:         204 ns/iter (+/- 60)
```

`str_to_string` は `iter` の外でto_stringしてしまうと `string_to_string` と同じことになってしまうので、 `iter` 内で `to_string` してみました。
当然、これが一番遅いですね。

[ここ](https://qiita.com/Mizunashi_Mana/items/db88cb0bff002abce1ae#%E6%96%87%E5%AD%97%E5%88%97%E3%81%A8%E6%96%87%E5%AD%97%E5%88%97%E3%82%B9%E3%83%A9%E3%82%A4%E3%82%B9%E3%81%AE%E9%81%95%E3%81%84)にもかいてあるけど

> しかしながら、パフォーマンス上ではスライスの作成はコピーが発生しませんが、Stringの作成はスライスの内容を一旦ベクタに変換する必要があります

`to_string` は slice -> Vector なので、メモリ再割当てが発生しているということですかね。

# まとめ
- 呼び出し元の変数の型がStringの場合は、&strでも&Stringでも、さほど変わらないので、受け取る側で使いやすい方で良い
- 呼び出し元の変数の型が&strの場合は、&Stringで受け取ろうとすると若干不利になる
- つまり、受け取る側で&strで良いのであれば (Stringでないと困る理由がなければ) &strにしておいた方が効率的

ということだろうか。

結局前回の

> 参照だけでいい場合は、&strを使う
> 文字列の中身を書き換えたい場合はStringを使うとよい

と同じことですね。
(というのを再認識した）
(`文字列の中身を書き換えたい場合` 以外にもStringにしかないメソッドを使いたいとかもあるかも)