rust
1pub fn replace(mut text: String, from: usize, to: usize) -> String { 2 text = text[..from].to_string(); 3 text.push_str(&mut text[from..from + 1]); 4 text.push_str(&mut text[to..]); 5 text 6}
上記の渡された文字列text
のfrom
番目にある文字をto
番目と置き換える関数を作っているんですが以下のようなエラーが出て困っています。
md
1cannot borrow `text` as mutable more than once at a time 2second mutable borrow occurs here
二つミュータブルな参照を作ることはできないのはわかるのですが&mut text[ ...
を&text[ ...
にするとミュータブルとして扱えないというようなエラーが出てしまいます。
どうすればよいでしょうか?
誰か教えてほしいです。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答2件
0
ベストアンサー
引数は参照専用にして、置き換え結果は返り値として得るのが簡便です。なお、参照にはString
ではなく&str
を使う方が、関数の応用範囲が広がるため好ましいです。
どうしても、引数をインラインで変更したければ、関数内で一度tmp変数に文字列をコピーして、それを参照しつつ、引数のtextをゼロから組み立てるべきでしょう。
Rust
1pub fn replace(text: &str, from: usize, to: usize) -> String { 2 let mut res = text[..from].to_string(); 3 res.push_str(&text[to..to + 1]); 4 res.push_str(&text[from + 1..to]); 5 res.push_str(&text[from..from + 1]); 6 res.push_str(&text[to + 1..]); 7 res 8} 9 10fn main() { 11 let s = replace("abcdefg", 2, 5); 12 println!("{}", s); //abfdecg 13}
投稿2021/10/10 08:43
編集2021/10/10 08:44総合スコア3266
0
Stringに文字列を二個以上追記したい
上の質問の直接の答えではないのですが、「渡された文字列text
のfrom
番目にある文字をto
番目と置き換える」のなら、slice
型のswap
メソッドを使うこともできます。
また、ご質問のようなスライスを使用したコードですと、文字列が英数文字だけのときは正しく動きますが、平仮名や漢字のような文字が含まれているとpanicするという問題があります。
まず、panicについて説明して、次にswap
メソッドについて説明します。
String
と&str
では内部データー表現としてUTF-8エンコーディングを使用しています。UTF-8では、たとえば"a"
は[0x61_u8]
の1バイトで表現されますが、"あ"
は[0xE3_u8, 0x81, 0x82]
の3バイトで表現されます。そして、&text[..from]
のように&str
からスライスを作るときは、インデックスとして「何文字目」ではなくて、「UTF-8バイト列の何バイト目」という意味になります。もし開始・終了インデックスのどちらかがUTF-8の文字の境目から外れているとpanicします。
rust
1// panicするコードの例 2fn main() { 3 // "あ" は [0xE3_u8, 0x81, 0x82] の3バイト 4 let text = "あいう".to_string(); 5 // 0バイト目だけを含むスライスを作成する 6 let mut res = &text[..1].to_string(); 7}
console
1thread 'main' panicked at 'byte index 1 is not a char boundary; 2it is inside 'あ' (bytes 0..3) of `あいう`'
もしreplace
関数が英数字だけを扱うのなら、ご質問のコードのようなやり方で問題ありません。しかし、それ以外の文字も扱う必要があるのなら別の方法を使うことになります。
いちばん確実な方法は&str
をいったんVec<char>
に変換することです。これにより、スライスのインデックス指定が、UTF-8の何バイト目ではなくて、何文字目(何個目のchar)という意味になります。
rust
1fn main() { 2 let text = "あいう".to_string(); 3 // &strからVec<char>へ変換する 4 let mut chars: Vec<_> = text.chars().collect(); 5 // Vec<char>のスライスからStringへ変換する 6 let mut res: String = chars[..1].iter().collect(); 7 println!("{}", res); // あ 8}
ただし、この方法ですと&str
からVec<char>
への変換とVec<char>
からString
への変換が必要になるので、実行時の効率は良くありません。
&str::char
の代わりに&str::char_indices
を使うともっと効率がよくなりますが、コードは少し複雑になります。(コード例は省略します)
次にswap
メソッドについて説明します。
slice
型のswap
メソッドは可変スライスのfrom
番目にある要素をto
番目と置き換えます。(ドキュメント)
これをchars()
とあわせて使うと、replace
の実装は以下のようになります。
rust
1pub fn replace(text: &str, from: usize, to: usize) -> String { 2 // &str型からVec<char>型へ変換する 3 let mut chars: Vec<_> = text.chars().collect(); 4 // from番目の文字とto番目を交換する 5 chars.swap(from, to); 6 // Vec<char>型からStringへ変換する 7 chars.into_iter().collect() 8} 9 10fn main() { 11 let s = replace("あいう", 0, 2); 12 assert_eq!(s, "ういあ"); 13}
また、もしreplace
関数が英数字だけを扱うのなら、asciiクレートを使うのがおすすめです。
rust
1// Cargo.tomlに以下を追加する 2// [dependencies] 3// ascii = "1.0" 4 5use ascii::{AsAsciiStr, AsciiStr, AsciiString}; 6 7// &AsciiStr型は&str型に相当するが、文字列が1バイトのASCIIコードで表される 8// 英数字記号だけで構成されていることを保証する 9// AsciiString型はString型に相当する 10pub fn replace(text: &AsciiStr, from: usize, to: usize) -> AsciiString { 11 // &AsciiStr型からAsciiString型へ変換する 12 let mut res = AsciiString::from(text); 13 // 可変スライスを取得しswapで交換する 14 res.as_mut_slice().swap(from, to); 15 res 16} 17 18fn main() { 19 // s0は&AsciiStr型。"abcdefg"は全て1バイトのASCIIコードで表せるので 20 // as_ascii_strは成功する。 21 let s0 = "abcdefg".as_ascii_str().unwrap(); 22 let s = replace(s0, 2, 5); 23 assert_eq!(s, "abfdecg"); 24 25 // "あいう"は1バイトのASCIIコードで表せないので&AsciiStrを作ろうとすると 26 // エラーになる 27 assert!("あいう".as_ascii_str().is_err()); 28}
投稿2021/10/10 11:37
総合スコア2055
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。