LoginSignup
5
2

More than 3 years have passed since last update.

[Rust] 文字列をコピーせずに文字列の一部を置換する (in-place replacement)

Last updated at Posted at 2019-07-18

Rustの文字列型にはreplaceというメソッドが実装されており、部分文字列の置換を行うことが出来る。
しかし、この関数は元の文字列を変更するのではなく、新たな文字列を生成するようだ。

この文字列の生成が無駄なように感じたので、Rustの練習も兼ねて元の文字列を書き換える関数を実装した。

実装

unsafe fn replace_inplace(s: &mut String, from: u8, to: u8) {
    let sv = s.as_bytes_mut();
    sv.iter_mut().filter(|x| **x == from).for_each(|x| *x = to);
}

使用方法

    let mut s = String::from("a\\b\\c");
    unsafe { replace_inplace(&mut s, b'\\', b'/'); }

    println!("{}", s);  // => "a/b/c"

ポイント

str型はas_bytes_mutメソッドを実装しており、このメソッドによってミュータブルなバイト列を取得できる。

let sv = s.as_bytes_mut();

しかし、これによって得られたバイト列を使えば、元の文字列を書き換えることが出来てしまう。しかももとの変数は借用されていないので、この関数を呼び出した後は、同じアドレスへの書き込み権限を有する変数が同時に2つ存在することになる。

したがって、いずれかの変数のライフタイムが切れるまではこのコードはunsafeであり、データ競合が起こらないように注意しなければならない。

例えば元の文字列を変更しようとした時に、メモリの再確保が行われるかもしれない。

// 文字列の長さを増やしているので、領域が足りない場合はメモリの再確保が行われる
s += "/d/e"

このとき、文字列の内部のデータのアドレスも新しいアドレスに置き換わるので、svが指すアドレスはもはや有効なアドレスではなくなる。またこの逆のパターン(取得したバイト列のメモリの再確保)もあるので注意しよう。

追記 (2019/7/16)

  • 変数の借用についての記述を修正
  • この実装だとUTF-8シークエンスを破壊する可能性があるので、関数自体をunsafeに変更

おわりに

今回は1文字だけの置換だったが、もっと複雑な置換も実装してみたいと思う。

5
2
3

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
5
2