LoginSignup
63
36

More than 1 year has passed since last update.

なぜRustはメモリ安全なのかをC言語のコードと考える

Last updated at Posted at 2022-11-16

追記: Twitterなどでご指摘等いただいているのをコメントしてますので、ぜひそちらもご覧ください!

これはなに

絶賛Rust勉強中の僕が、「なんでRustはメモリ安全って言われているの?」と聞かれたので、実際にメモリ安全ではない(?)C言語のコードを並べてみて考える記事です。

C言語の場合

メモリリーク

以下のコードはメモリリークを起こす可能性があります。

memory_leak.c
#include <stdio.h>
#include <stdlib.h>

#define STR_BUF_SIZE 100000000

char *str_new() { return calloc(STR_BUF_SIZE, sizeof(char)); }

int main() {
  for (int i = 0; i < 100000000; i++) {
    char *str = str_new();
    snprintf(str, STR_BUF_SIZE, "[number: %d]", i);

    printf("%s\n", str);
  }

  return 0;
}

メモリリークを起こす原因としては、callocによってアロケートされたメモリが不要になっているにも関わらず、開放されないためです。

バッファオーバーラン

以下のコードは、確保したメモリの領域外にアクセスがされてしまう例です。

buffer_overrun.c
#include <stdio.h>

#define BUF_SIZE 16

int main() {
  int buf[BUF_SIZE];

  for (int i = 0; i < BUF_SIZE + 2; i++) {
    buf[i] = i;
    printf("buf[%d]: %d\n", i, buf[i]);
  }

  return 0;
}

配列bufのサイズ16を超えて書き込みが行われてしまっており、また、エラーにならずに実行できてしまいます。

Rustの場合

メモリリーク

Rustには所有権・ライフタイムの仕組みがあり、スコープをでると自動的に開放(drop)されます。

memory_leak.rs
fn main() {
    for i in 1..10000000 {
        let str = format!("[number: {}]", i);
        println!("{}", str);
    }
}

そのため、C言語の例のように、動的に確保したメモリを開放し忘れる心配がいらなくなります。

また、ライフタイムの仕組みによって、ダングリングポインタなども考える必要がなくなります。

バッファオーバーラン

Rustでは、実行時に確保されたメモリ領域内であるかのチェックが走るようになります。

buffer_overrun.rs
const BUF_SIZE: usize = 16;

fn main() {
    let mut buf = [0; BUF_SIZE];

    for i in 0..(BUF_SIZE + 2) {
        buf[i] = i;
        println!("buf[{}]: {}", i, buf[i]);
    }
}

なお、実行時チェックのオーバーヘッドがあるようなので、イテレータなどを使うようにするのが良い様です。

さいごに

他にもあると思いますが、これでRustのメモリ安全のイメージが湧くと嬉しいです。
間違いなどありましたら、コメント・編集リクエストから教えていただけると勉強になります。

Refs

63
36
10

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
63
36