露出した Unsafe Rust

// 著作権 2026 Google LLC
// SPDX-License-Identifier: Apache-2.0

pub fn copy(dest: &mut [u8], source: *const u8) {
    let source = {
        let mut len = 0;

        let mut end = source;
        while unsafe { *end != 0 } {
            len += 1;
            end = unsafe { end.add(1) };
        }

        unsafe { std::slice::from_raw_parts(source, len + 1) }
    };

    for (dest, src) in dest.iter_mut().zip(source) {
        *dest = *src;
    }
}

fn main() {
    let a = [114, 117, 115, 116].as_ptr();
    let b = &mut [82, 85, 83, 84, 0];

    println!("{}", String::from_utf8_lossy(b));
    copy(b, a);
    println!("{}", String::from_utf8_lossy(b));
}

ある場所から別の場所へバイトをコピーするという機能自体は同じままです。

「ただし、スライスを手動で作成する必要があります。そのためには、まず データの終端を見つける必要があります。」

「今回はテキストを扱っているので、C の慣習であるヌル終端 文字列を使います。」

コードをコンパイルしてください。出力が同じままであることを確認してください。

「健全でない関数でも、入力によっては正しく動作することがあります。テストが 通るからといって、その関数が健全であるとは限りません。」

「何か問題点に気づく人はいますか?」

  • 可読性: コードをすばやく見て把握しにくい
  • source ポインタが null かもしれない
  • source ポインタがダングリングしているかもしれない。つまり、解放済みまたは未初期化の メモリを指している可能性がある
  • source がヌル終端されていないかもしれない

「関数シグネチャを変更できないと仮定した場合、これらの問題に対処するために コードへどのような改善を加えられるでしょうか?」

  • null ポインタ: null チェックを追加し、早期リターンする (if source.is_null() { return; })
  • 可読性: 「最初の null バイトを見つける」処理を自前で実装するのではなく、 十分にテストされたライブラリを使う

「ただし、一部の安全要件は防御的にチェックすることが不可能です。 たとえば:」

  • ダングリングポインタ
  • ヌル終端バイトが存在しないこと

「この関数をどのように健全にできるでしょうか?」

  • 次のいずれか
    • source 入力引数の型を、長さが既知のものに変更する。つまり、前の例のように スライスを使う。
  • または
    • 関数を unsafe としてマークする
    • 安全性の事前条件を文書化する