Rust
60
どのような問題がありますか?

投稿日

更新日

Rustのcharと&strとStringが難しい

RustのcharとStringと&strがすごく難しい。さすが学習難易度が高いRust。それでも、たぶん慣れの問題で、覚えて慣れるのが良い方法かも。

charとStringと&strの関係

Rustでは、Stringと&strがたくさん出てくる。この三者の違いを一言で語ってみよう。

  • Stringは文字列を表すことができる。
  • &strはUTF-8の配列のスライス(&[u8])であり文字列リテラルを表すのに使われる。
  • charはユニコードの1文字を表現できる。

Stringと&strの関係

文字列リテラルは&strである。

prints.rs
let s1: &str = "abc";
println!("{}", s1); // abc

Stringと&strは容易に変換が可能。

s2s.rs
// &str → String
let s1: String = String::from("abc");
println!("{}", s1);

&str → String → &strと変換する場合。

sss.rs
    let s1: &str = "abc";
    let s2: String = String::from(s1); // &str -> String
    let s3: &str = &s2; // String -> &str
    println!("{}", s3);

このように、&strとStringは比較的簡単に変換できる。

charをStringに変換

charをStringに変換するには、to_stringメソッドが使える。

c2s.rs
    let c: char = 'a';
    let cs: String = c.to_string();
    println!("{}", &cs); // → a

Vec<char>をStringに変換

サイズ可変な配列であるcharのベクタ型であるVec<char>。これをStringに変換するには、iter().collect()を使う。

vec2s.rs
// Vec<char>を初期化
let c = vec!['a', 'b', 'c'];
// Vec<char>をStringに変換
let cs: String = c.iter().collect();
println!("{}", &cs); // → abc

Stringや&strをVec<char>に変換

&strをVec<char>に変換してみる。

s2vec.rs
// &str → Vec<char>
let cs: Vec<char> = "abcd".chars().collect();
// Vec<char>であれば、添字アクセス可能
let c1: char = cs[0];
let c2: char = cs[1];
println!("{}-{}", c1.to_string(), c2.to_string());

同じように、StringをVec<char>に変換することも同様の方法で可能。

s2vec2.rs
    let s = String::from("abc");
    let cs: Vec<char> = s.chars().collect();
    let c1: char = cs[0];
    println!("{}", c1.to_string());

&[char]をStringに変換する場合も、iter().collect()でいける。

vec2s2.rs
    let cs: &[char] = &['a', 'b', 'c'];
    let s: String = cs.iter().collect();
    println!("{}", s); // abc

String に charを追加する

append.rs
    let mut s = String::from("abc");
    s.push('d');
    println!("{}", s); // abcd

&str に char を追加したい

&strは残念ながら変更できないので一度Stringにしてからcharを追加する。

append2.rs
    let s = "abc";
    let mut ss = String::from(s);
    ss.push('d');
    println!("{}", ss); // abcd

Vec<char>とVec<char>を足す

mut Vec<char>であれば、extendで追記が可能。

append3.rs
    let mut a: Vec<char> = "abc".chars().collect();
    let b: Vec<char> = "def".chars().collect();
    a.extend(b.iter()); // bの内容をaに追加
    let c: String = a.iter().collect();
    println!("{}", c); // abcdef

似たような感じだけど、Vec.append()も使える。

append3a.rs
    let mut a: Vec<char> = "abc".chars().collect();
    let b: Vec<char> = "def".chars().collect();
    a.append(&mut b.clone());
    println!("{}", a.iter().collect::<String>()); // abcdef

Stringに変換してconcatするという手もある、冗長だけど。

append4.rs
    let a: Vec<char> = "abc".chars().collect();
    let b: Vec<char> = "def".chars().collect();
    let c: String = [
        a.iter().collect::<String>(),
        b.iter().collect::<String>()
    ].concat();
    println!("{}", c); // abcdef

上記で、Vec<char>のaとbの内容を変更したくない場合は、新しい変数cを作成する。

append5.rs
    let a = "abc".chars().collect::<Vec<char>>();
    let b = "def".chars().collect::<Vec<char>>();
    // c = a + b
    let mut c: Vec<char> = Vec::new();
    c.append(&mut a.clone());
    c.append(&mut b.clone());
    // print(c)
    let s = c.iter().collect::<String>();
    println!("{}", s); // abcdef

上記と全く同じで別の方法。好みにもよるけど、extend()使った方がシンプルな感じがする。

append6.rs
    let a = "abc".chars().collect::<Vec<char>>();
    let b = "def".chars().collect::<Vec<char>>();
    // c = a + b
    let mut c: Vec<char> = Vec::new();
    c.extend(a.iter());
    c.extend(b.iter());
    // print(c)
    let s = c.iter().collect::<String>();
    println!("{}", s); // abcdef

コメントで、chain()を使う方法を教えていただきました。この方法も良いですね。

append7.rs
    let a = "abc".chars().collect::<Vec<char>>();
    let b = "def".chars().collect::<Vec<char>>();

    // String が必要な場合
    let s = a.iter().chain(b.iter()).collect::<String>();
    println!("{}", s); // abcdef

    // Vec<&char> が必要な場合
    let c = a.iter().chain(b.iter()).collect::<Vec<&char>>();
    println!("{:?}", c); // ['a', 'b', 'c', 'd', 'e', 'f']

    // Vec<char>が必要で、aとbの所有権を奪う場合
    let d: Vec<char> = a.into_iter().chain(b.into_iter()).collect();
    println!("{:?}", d); // ['a', 'b', 'c', 'd', 'e', 'f']

参考

『Rustの書きかた・作りかた』という本を書きました。良かったらそちらも見てみてください!!

その他、文字列操作で役立つリンク

ユーザー登録して、Qiitaをもっと便利に使ってみませんか。
  1. あなたにマッチした記事をお届けします
    ユーザーやタグをフォローすることで、あなたが興味を持つ技術分野の情報をまとめてキャッチアップできます
  2. 便利な情報をあとで効率的に読み返せます
    気に入った記事を「ストック」することで、あとからすぐに検索できます
kujirahand
「クジラ飛行机」では技術文書の執筆やアプリの開発をしています。代表作は日本語プログラミング言語「なでしこ」、テキスト音楽「サクラ」。最近、Rustの本書きました!

コメント

(編集済み)
リンクをコピー
このコメントを報告

はじめまして. 「Vec<char>とVec<char>を足す」節のコードで, a, b が変更不可の場合は次のような書き方をすると簡潔になるのではないかと思うのですが, いかがでしょう? (doc)

fn main() {
    let a = "abc".chars().collect::<Vec<char>>();
    let b = "def".chars().collect::<Vec<char>>();

    // Vec<char> が欲しい場合
    let c = a.iter().chain(b.iter()).collect::<Vec<_>>();
    println!("{:?}", c); // ['a', 'b', 'c', 'd', 'e', 'f']

    // String が欲しい場合
    let s = a.iter().chain(b.iter()).collect::<String>();
    println!("{}", s); // abcdef
}
1
リンクをコピー
このコメントを報告

@osanshouo さま
ありがとうございます。確かに、chain使うと簡潔になりますね!教えてくださり、ありがとうございます。

0
リンクをコピー
このコメントを報告

@kujirahand さん
お役に立てたようで何よりです. ただ, すいません, 私のコメントのコードは正確には Vec<char> でなくて Vec<&char> でしたので訂正させてください.

let _: Vec<&char> = a.iter().chain(b.iter()).collect::<Vec<_>>(); // a, b の借用
let _: Vec<char> = a.into_iter().chain(b.into_iter()).collect::<Vec<_>>(); // a, b の所有権を奪う
1
リンクをコピー
このコメントを報告

@osanshouo さん、修正コードもありがとうございます!
into_iterを使うと所有権が移転するんですね、勉強になります!

1
どのような問題がありますか?
あなたもコメントしてみませんか :)
ユーザー登録
すでにアカウントを持っている方はログイン
60
どのような問題がありますか?
ユーザー登録して、Qiitaをもっと便利に使ってみませんか

この機能を利用するにはログインする必要があります。ログインするとさらに下記の機能が使えます。

  1. ユーザーやタグのフォロー機能であなたにマッチした記事をお届け
  2. ストック機能で便利な情報を後から効率的に読み返せる
ユーザー登録ログイン
ストックするカテゴリー