23
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

お題は不問!Qiita Engineer Festa 2023で記事投稿!

【Rust】&strとString両方とも渡す方法 (とCowの話し)

Posted at

はじめに

Rustでは文字列リテラル(&str)と文字列(String)は別の型です。

引数の型が&strの場合、String&を付けて参照を渡す必要があります。

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

fn main() {
    let s1 = "Hello &str!!";
    let s2 = "Hello String!!".to_string();
    print(s1);
    print(&s2);
}

引数の型がStringの場合、&strto_stringStringに変換する必要があります。

fn print(s: String) {
    println!("{}", s);
}

fn main() {
    let s1 = "Hello &str!!";
    let s2 = "Hello String!!".to_string();
    print(s1.to_string());
    print(s2);
}

この両方を渡す方法は色々ありますが、今回はCowを使用した方法を紹介します。

結論

Into<Cow<'a, str>>というトレイト境界で両方とも渡せるようになります。

use std::borrow::Cow;

fn print<'a, T: Into<Cow<'a, str>>>(s: T) {
    println!("{}", s.into());
}

fn main() {
    let s1 = "Hello &str!!";
    let s2 = "Hello String!!".to_string();
    print(s1);
    print(s2);
}

Cowって何?🐄

CowとはClone on writeの略で「参照の保持」と「値の所有」のどちらも可能な列挙体です。
以下の様に定義されています。

pub enum Cow<'a, B>
where
    B: 'a + ToOwned + ?Sized,
{
    Borrowed(&'a B),
    Owned(<B as ToOwned>::Owned),
}

Cow::Borrowedには文字列リテラルを、Cow::Ownedには文字列を渡します。

    let c1: Cow<'_, str> = Cow::Borrowed("Literal");
    let c2: Cow<'_, str> = Cow::Owned("String".to_string());

Cow::fromメソッドから生成もできます。

    let c1: Cow<'_, str> = Cow::from("Literal"); // Cow::Borrowed
    let c2: Cow<'_, str> = Cow::from("String".to_string()); // Cow::Owned

Cowを使うことで、型を抽象化して扱うことが可能です。

遅延クローン

Cowには遅延クローンという便利な機能があります。
参照に対して書き込みをしたタイミングで、データのコピーが行われる機能です。

use std::borrow::Cow;

fn print_cow<'a>(c: &Cow<'a, str>) {
    match c {
        Cow::Borrowed(s) => {
            println!("Cow::Borrowed {s}");
        }
        Cow::Owned(s) => {
            println!("Cow::Owned {s}");
        }
    }
}

fn main() {
    let s = "Hello, World!!";
    let mut cow = Cow::Borrowed(s);
    print_cow(&cow);
    cow.to_mut().push_str("Push String"); // ここで文字列リテラルがヒープ領域にコピーされる
    print_cow(&cow);
}

上記のコードの実行結果は以下の様になります。

Cow::Borrowed Hello, World!!
Cow::Owned Hello, World!!Push String

文字列リテラルの参照を保持していた変数cowですが、文字列の追加後は値の所有に変わっています。
to_mut関数で可変参照を取得して文字列を追加したタイミングで、文字列リテラルがヒープ領域にコピーされその領域に書き込みを行っています。

このように必要な時にデータのコピーが行われるので、あらかじめヒープ領域を確保しておくといったことが不要になります。

参考文献

23
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?