所有権? String と 文字列スライス

文字リテラル Hello から String を作成し、一部を文字列スライスとして切り出した後、String スコープ外で文字列スライスを使う。というシナリオを考えたが、以下のようにコンパイルエラーが発生する。文字列スライスが参照している大元の String がスコープを抜ける際に Drop してしまうためだ。

fn main() {
    let sub: &str;
    {
        let s = String::from("Hello");
        sub = &s[0..2];
//             ^ borrowed value does not live long enough
    }

    println!("{}", sub);
//                 --- borrow later used here
}

文字列スライスを clone() すれば行けるか?と思ったが、文字列スライスは clone() メソッドを実装していないようだ。同様に copy() メソッドも実装されていなかった。

fn main() {
    let sub: &str;
    {
        let s = String::from("Hello");
        sub = &s[0..2].clone();
//                     ^^^^^ method not found in `str`
    }

    println!("{}", sub);
}

一度文字列スライスから String を作成し、それを as_str() で再度文字列スライス化しても、新たに生成した String はスコープを抜ける際に Drop() されてしまうので、同じだった。

fn main() {
    let sub: &str;
    {
        let s = String::from("Hello");
        sub = String::from(&s[0..2]).as_str();
//            ^^^^^^^^^^^^^^^^^^^^^^         - temporary value is freed at the end of this statement
//            |
//            creates a temporary which is freed while still in use
    }

    println!("{}", sub);
//                 --- borrow later used here
}

文字列スライスを取得するのをあきらめて、文字列スライスを String にしたものを取得するようにすると、コンパイルが通る。

fn main() {
    let sub: String;
    {
        let s = String::from("Hello");
        sub = String::from(&s[0..2]);
    }

    println!("{}", sub);
}

to_string() のほうが String::from() より簡潔に書けてよさそう。

fn main() {
    let sub: String;
    {
        let s = String::from("Hello");
        sub = s[0..2].to_string();
    }

    println!("{}", sub);
}

というか、今回の例ではこれでもいいのか。簡潔に書きすぎて、いったい何がしたいんだ。。。というコードになるけど。。。

fn main() {
    let sub: String;
    {
        sub = "Hello"[0..2].to_string();
    }

    println!("{}", sub);
}

参考資料

qiita.com