Rust's Miri and Linked List




Miri

Rust๋Š” "memory safe"ํ•œ ์–ธ์–ด๋กœ ์‚ฌ๋ž‘ ๋ฐ›๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ฐธ์กฐ์ž(reference) ์™€ ๋นŒ๋ฆผ(borrow) ๊ทธ๋ฆฌ๊ณ  ๋ผ์ดํ”„ํƒ€์ž„(lifetime)์„ ํ†ตํ•ด Rust๋Š” ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ถœ(memory leak) ์—†๋Š” ์•ˆ์ •์ ์ด๊ณ  ๋งŒ์กฑ์Šค๋Ÿฌ์šด ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ฒฝํ—˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋•Œ๋ก  Rust์˜ ๋ฉ”๋ชจ๋ฆฌ ์•ˆ์ •์„ฑ ๊ทœ์น™์„ ์œ„๋ฐฐํ•˜๊ณ  ์‹ถ์€, ๋˜๋Š” ๊ทธ๋ž˜์•ผ๋งŒ ํ•˜๋Š” ๋•Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋•Œ Rust๋Š” usafe๋ฅผ ํ†ตํ•ด ๊ทœ์น™ ์œ„๋ฐ˜์„ ํ—ˆ์šฉํ•˜์ฃ . unsafe scope ์•ˆ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์ผ์— ๋Œ€ํ•ด Rust๋Š” ์กฐ๊ธˆ๋„ ์ฑ…์ž„์ง€์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ปดํŒŒ์ผ์ด ๋˜๋”๋ผ๋„ ๋Ÿฐํƒ€์ž„์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์˜ค๋ฅ˜๋Š” ์˜จ์ „ํžˆ ์ž‘์„ฑ์ž์˜ ์ฑ…์ž„์ž…๋‹ˆ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ๋Ÿฐํƒ€์ž„ ์ „์— ๋ฏธ๋ฆฌ๋ฏธ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๊ฐ€๋Šฅ์„ฑ์„ ์ฒดํฌํ•ด๋ณผ ์ˆ˜๋Š” ์—†์„๊นŒ์š”?

์˜ค๋Š˜ ์†Œ๊ฐœํ•˜๋ ค๋Š” ๊ฒƒ์€ Rust์˜ Miri ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค. ํ•œ๊ตญ์–ด๋กœ ๋ฏธ๋ฆฌ์—์„œ ๋”ฐ์˜จ ๋ง์€... ๋‹น์—ฐํžˆ ์•„๋‹™๋‹ˆ๋‹ค. Miri๋Š” "Mid-level intermediate representation(MIR)์„ ์œ„ํ•œ interpreter"๋ผ๋Š” ๋œป์œผ๋กœ, ๋Ÿฐํƒ€์ž„ ์‹œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์˜ค๋ฅ˜๋ฅผ ๋ฏธ๋ฆฌ ๊ฒ€์‚ฌํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. Miri๊ฐ€ ์ง€์›ํ•˜๋Š” ๊ธฐ๋Šฅ์—๋Š” ๋งŽ์€ ์ข…๋ฅ˜๊ฐ€ ์žˆ๊ณ , ์—ฌ์ „ํžˆ ํ™œ๋ฐœํ•˜๊ฒŒ ๋ฐœ์ „์ค‘์ด๋ฉฐ, ๋‹น์—ฐํ•˜๊ฒŒ๋„ ๋ชจ๋“  ๋ฐœ์ƒ ๊ฐ€๋Šฅํ•œ ์˜ค๋ฅ˜๋ฅผ ๋ฏธ๋ฆฌ ์ฒดํฌํ•ด์ฃผ์ง€๋Š” ๋ชปํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋งŒ ๋ฉ”๋ชจ๋ฆฌ ์˜ค๋ฅ˜๋ฅผ ๋ถˆ๋Ÿฌ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ๊ฐ€ ์—†๋Š”์ง€ ๋ฏธ๋ฆฌ ํ™•์ธํ•˜๋Š” ์šฉ๋„๋กœ ํ†กํ†กํžˆ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํŠนํžˆ ๋ถˆ๊ฐ€ํ”ผํ•˜๊ฒŒ unsafe ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค๋ฉด Miri๋กœ ๊ฒ€์‚ฌํ•ด๋ณด๋Š” ๊ฒŒ ํ•„์ˆ˜๊ฒ ์ฃ !

Using Miri

Miri๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” rust์˜ nightly ํˆด์ฒด์ธ์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ๋ช…๋ น์–ด๋กœ nightly ๋ฒ„์ „๊ณผ miri๋ฅผ ํ•จ๊ป˜ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.

rustup +nightly component add miri

์ด์ œ miri๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. cargo run์ด๋‚˜ cargo test ๋ช…๋ น์–ด์ฒ˜๋Ÿผ, cargo miri run๊ณผ cargo miri test ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰ Miri๋Š” test run์— ์“ฐ๊ฑฐ๋‚˜ binary ํŒŒ์ผ์˜ run์— ์“ฐ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ๊ฐ์˜ ๋Ÿฐํƒ€์ž„ ๊ณผ์ •์—์„œ ์˜ค๋ฅ˜๋ฅผ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ด์ฃ .

๊ธฐ๋ณธ์ ์ธ memory leak์ด ๋ฐœ์ƒํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

fn main() {
let x: *mut i32 = Box::into_raw(Box::new(0));
unsafe {
  *x = 10;
}
}

์œ„ ์ฝ”๋“œ๋ฅผ cargo run์œผ๋กœ ์‹คํ–‰ํ•˜๋ฉด ๋ฌธ์ œ ์—†์ด ์ปดํŒŒ์ผ ๋ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์‚ฌ์‹ค์€ raw pointer์ธ x๊ฐ€ ์ œ๋Œ€๋กœ drop๋˜์ง€ ์•Š์•„ memory leak์ด ๋ฐœ์ƒํ•˜๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. cargo miri run์„ ์‹คํ–‰ํ•˜๋ฉด "error: memory leaked"์™€ ๊ฐ™์€ ์—๋Ÿฌ ๋ฌธ๊ตฌ๊ฐ€ ๋œจ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. Box<>์— ์žกํ˜€์žˆ๋Š” raw pointer๋ฅผ dropํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

fn main() {
let x: *mut i32 = Box::into_raw(Box::new(0));
unsafe {
  *x = 10;
  let _ = Box::from_raw(x);
}
}

๋น„์Šทํ•œ ๊ฒฝ์šฐ๋กœ ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ๋ด…์‹œ๋‹ค:

fn main() {
let x: *mut i32 = Box::into_raw(Box::new(0));
unsafe {
  let _ = Box::from_raw(x);
  let y = *x + 10;
  println!("{}", y);
}
}

cargo run์€ ๋ฌธ์ œ ์—†์ด ์ปดํŒŒ์ผ ๋˜์ง€๋งŒ, cargo miri run์€ "x๊ฐ€ ์ด๋ฏธ freed๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— y๊ฐ€ dangling pointer์— ์ ‘๊ทผํ•˜๊ณ  ์žˆ๋‹ค"๋Š” memory access failed ์˜ค๋ฅ˜๋ฅผ ์•Œ๋ ค์ค๋‹ˆ๋‹ค.

์ฐธ๊ณ ๋กœ ์œ„ ์ฝ”๋“œ์—์„œ cargo run์„ ๊ทธ๋ž˜๋„ ์‹คํ–‰ํ•ด๋ณด์„ธ์š”. ํ•  ๋•Œ๋งˆ๋‹ค ๊ฐ๊ธฐ ๋‹ค๋ฅธ ๊ฐ’์ด y์˜ ๊ฐ’์œผ๋กœ ์ถœ๋ ฅ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. (๊ณ„์† 10์ด ๋‚˜์˜ฌ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.)

์ด์ œ ํ•œ๋ฒˆ test ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด ๋ด…์‹œ๋‹ค.

#![allow(unused)]
fn main() {
#[test]
fn no_memory_leak() {
  let x: *mut i32 = Box::into_raw(Box::new(0));
  unsafe {
    *x = 10;
    let _ = Box::from_raw(x);
  }
}

#[test]
// #[cfg_attr(miri, ignore)]
fn yeah_memory_leak() {
  let x: *mut i32 = Box::into_raw(Box::new(0));
  unsafe {
    *x = 10;
  }
}
}

cargo test ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋‘ test ํ•จ์ˆ˜ ๋ชจ๋‘ ๋ฌธ์ œ ์—†์ด ์ž‘๋™ํ•˜์ง€๋งŒ, cargo miri test๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด yeah_memory_leak()์— memory leak์ด ๋ฐœ์ƒํ•จ์„ ์•Œ๋ ค์ค๋‹ˆ๋‹ค. ๋งŒ์ผ ์ด๊ฒƒ์ด ์˜๋„๋œ ๊ฒƒ์ด๊ณ  miri๊ฐ€ ํ•ด๋‹น ํ•จ์ˆ˜๋ฅผ ๋ฌด์‹œํ•˜๊ธธ ๋ฐ”๋ž€๋‹ค๋ฉด, #[cfg_attr(miri, ignore)] ํ”Œ๋ž˜๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

Stacked borrow

์•ž์„œ ๋ณธ ๊ธฐ๋ณธ์ ์ธ ๋ฌธ์ œ์™€ ํ•จ๊ป˜, miri๋Š” stacked borrow์— ๋Œ€ํ•œ ๊ฒ€์‚ฌ๋ฅผ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค. stacked borrow๋Š” ๊ฐ„๋‹จํžˆ ๋งํ•ด ๋ฉ”๋ชจ๋ฆฌ ๋นŒ๋ฆผ์ด stack ๊ตฌ์กฐ๋กœ ์ €์žฅ๋˜์–ด ์žˆ๋Š” ์ƒํƒœ๋ฅผ ๋œปํ•ฉ๋‹ˆ๋‹ค. stack ๊ตฌ์กฐ๋Š” ์ „ํ˜•์ ์ธ ํ›„์ž…์„ ์ถœ ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค. ๊ฐ€์žฅ ๋Šฆ๊ฒŒ ๋“ค์–ด์˜จ ๋ฐ์ดํ„ฐ๊ฐ€ ๊ฐ€์žฅ ๋จผ์ € ๋‚˜๊ฐ€์•ผ ํ•˜์ฃ . ๋งŒ์ผ ๋” ๋จผ์ € ๋“ค์–ด์˜จ ๋ฐ์ดํ„ฐ๊ฐ€ ์ž์‹ ๋ณด๋‹ค ๋Šฆ๊ฒŒ ๋“ค์–ด์˜จ (stack์˜ ๋” ์œ„์— ์žˆ๋Š”) ๋ฐ์ดํ„ฐ๋ณด๋‹ค ๋จผ์ € ์ ‘๊ทผ๋œ๋‹ค๋ฉด, ์‚๋น…-๐Ÿšจ ๋ฉ”๋ชจ๋ฆฌ ์˜ค๋ฅ˜์ž…๋‹ˆ๋‹ค.

์˜ˆ์ปจ๋Œ€ Rust์˜ safe code์—์„œ๋„ ๋‹ค์Œ ์ฝ”๋“œ๋Š” ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์Šน์ธํ•˜์ง€ ์•Š์ฃ .

fn main() {
let mut a: i32 = 10;
let b = &mut a;

a += 20;
*b += 20;
}

๋ฐ˜๋ฉด ๋‹ค์Œ์€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

fn main() {
let mut a: i32 = 10;
let b = &mut a;

*b += 20;
a += 20;
}

Unsafe ์ฝ”๋“œ๋Š” ์ปดํŒŒ์ผ๋Ÿฌ์— ์˜ํ•ด ๊ฒ€์‚ฌ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋Œ€์‹  Miri๋ฅผ ์ด์šฉํ•ด stacked borrow ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•ด๋ณผ ์ˆ˜ ์žˆ์ฃ . ๋‹ค์Œ ์˜ˆ์‹œ๋Š” ์œ ๋ช…ํ•œ rust book ์ค‘ ํ•˜๋‚˜์ธ "Too Many Linked Lists"์˜ ๊ด€๋ จ ์ฑ•ํ„ฐ๋ฅผ ์ฐธ๊ณ ํ–ˆ์Šต๋‹ˆ๋‹ค.

#![allow(unused)]
fn main() {
#[test]
fn ok_stacked_borrow() {
  let mut a: i32 = 10;
  unsafe {
    let b: *mut i32 = &mut a;
    let c: *mut i32 = &mut *b;

    *c += 10;
    *b += 10;
  }
  assert_eq!(a, 30);
}

#[test]
fn bad_stacked_borrow() {
  let mut a: i32 = 10;
  unsafe {
    let b: *mut i32 = &mut a;
    let c: *mut i32 = &mut *b;

    *b += 10;
    *c += 10;
  }
  assert_eq!(a, 30);
}
}

cargo test์€ ๋‘ ํ…Œ์ŠคํŠธ ๋ชจ๋‘ ํ†ต๊ณผ์‹œํ‚ต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ cargo miri test๋Š” bad_stacked_borrow()์— ๋Œ€ํ•ด "์ ‘๊ทผํ•˜๋ ค๋Š” ์œ„์น˜๊ฐ€ borrow stack์— ์—†๋‹ค"๋Š” ๋‚ด์šฉ์˜ ์˜ค๋ฅ˜ ์ฝ”๋“œ๋ฅผ ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค. c๊ฐ€ borrow stack์˜ ๊ฐ€์žฅ ์œ„์— ์žˆ๊ณ  ์•„์ง ์ ‘๊ทผ๋  ๊ธฐํšŒ๊ฐ€ ๋‚จ์•„ ์žˆ๋Š”๋ฐ b์— ๋จผ์ € ์ ‘๊ทผํ•˜๊ณ  ์žˆ์œผ๋‹ˆ ๋ฉ”๋ชจ๋ฆฌ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒƒ์ด์ฃ .

Linked List

Rust์—์„œ unsafe raw pointer๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋Š” ๊ฒฝ์šฐ ์ค‘ ํ•˜๋‚˜๋Š” linked list๋ฅผ ๊ตฌํ˜„ํ•  ๋•Œ์ž…๋‹ˆ๋‹ค. linked list๋Š” ์ผ๋ฐ˜์ ์ธ list๋‚˜ vector ๊ตฌ์กฐ์ฒ˜๋Ÿผ ํ•˜๋‚˜์˜ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ์•ˆ์— ๊ฐœ๋ณ„ ๋ฐ์ดํ„ฐ๋“ค์ด index๋ฅผ ๊ฐ€์ง€๊ณ  ๋‹ด๊ฒจ ์žˆ๋Š” ๊ฒƒ๊ณผ ๋‹ฌ๋ฆฌ, ๊ฐœ๋ณ„ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๋ฅดํ‚ค๋ฉด์„œ(pointer๋ฅผ ํ†ตํ•ด) ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋Š” ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค.

์ด๋•Œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๋ฅดํ‚ค๋Š” pointer๋ฅผ raw pointer๋กœ ๊ตฌํ˜„ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋ฌผ๋ก ! ๊ผญ raw pointer๋ฅผ ์จ์•ผ ํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ Rust๊ฐ€ ๊ฐ•์ œํ•˜๋Š” ๋ฉ”๋ชจ๋ฆฌ ์•ˆ์ •์„ฑ์„ ๊ทธ๋Œ€๋กœ ์ค€์ˆ˜ํ•˜๋ฉด์„œ linked list๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์€ ์˜คํžˆ๋ ค ๋ณต์žกํ•˜๊ณ , ๋น„ํšจ์œจ์ ์ธ ์ผ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ ๊ผฌ๋ฆฌ์™€ ๋จธ๋ฆฌ๊นŒ์ง€ ์„œ๋กœ ์—ฐ๊ฒฐ๋œ Circular list๋ฅผ Rust์˜ safe code๋กœ ์ž‘์„ฑํ•˜๋Š” ์ผ์€ ๊ฝค๋‚˜ ๋ฒˆ๊ฑฐ๋กญ์Šต๋‹ˆ๋‹ค. ์•ž์„œ ์–ธ๊ธ‰ํ•œ "Too Many Linked Lists" ์ž์ฒด๊ฐ€ Rust์—์„œ linked list ๋งŒ๋“œ๋Š” ์ผ์˜ ๊ธฐ์จ๊ณผ ์Šฌํ””์— ๋Œ€ํ•œ ๋‚ด์šฉ์ด๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ๋Š” ํ•œ๋ฒˆ ๊ฐ„๋‹จํ•œ Circular linked list๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. safe code์™€ unsafe code๋กœ ๊ฐ๊ฐ ๊ตฌํ˜„ํ•ด๋ณด๊ณ , miri test๋ฅผ ์ง„ํ–‰ํ•ด๋ด…์‹œ๋‹ค. ํ•ด๋‹น ์ฝ”๋“œ๋Š” https://github.com/AcheuleanGraph/tech.blob์—์„œ ์ฐพ์•„๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์™€ ์—ฐ๊ด€ ํ•จ์ˆ˜์— ๋Œ€ํ•œ ๋„ˆ๋ฌด ์ž์„ธํ•œ ์„ค๋ช…์€ ์ƒ๋žตํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

Comparison

๋จผ์ € unsafe ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•œ circular list์ž…๋‹ˆ๋‹ค. Rust์—์„œ ๋‹ค์ค‘ ์ฐธ์กฐ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ๋งŽ์ด ์“ฐ๋Š” Rc<RefCell<>> ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค:

#![allow(unused)]
fn main() {
pub struct NodeA<T: Clone>{
  pub data: T,
  pub prev: Option<Rc<RefCell<NodeA<T>>>>,
  pub i: usize,
  pub next: Option<Rc<RefCell<NodeA<T>>>>
}
}

ํ•˜๋‚˜์˜ ๋…ธ๋“œ๊ฐ€ ์ด์ „ ๋…ธ๋“œ(prev)์™€ ๋‹ค์Œ ๋…ธ๋“œ(next)๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. i๋Š” ์ „์ฒด ๋…ธ๋“œ๋ฅผ ์ˆœํšŒํ•  ๋•Œ, ์ˆœํšŒ์˜ ์‹œ์ ๊ณผ ๊ธฐ์ ์„ ๊ธฐ์–ตํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ ๋…ธ๋“œ๋งˆ๋‹ค ๊ฐ–๊ฒŒ ํ•˜๋Š” ๊ณ ์œ ๊ฐ’์ž…๋‹ˆ๋‹ค. ๋งŒ์ผ ์ด๋Ÿฐ ํ‘œ์‹์ด ์—†๋‹ค๋ฉด ์‹ค์ œ ์‚ฌ์šฉ์‹œ circular list๋Š” ๋ฌดํ•œ ์ˆœํ™˜์„ ์ผ์œผํ‚ฌ ๊ฒƒ์ด๊ณ  ์‰ฝ๊ฒŒ memory leak์ด ๋ฐœ์ƒํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ฐธ๊ณ ๋กœ ์ œ๋„ˆ๋ฆญ <T>๋Š” ๊ฐ ๋…ธ๋“œ๊ฐ€ ๊ฐ–๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œํ˜„ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์•„! Rc์™€ RefCell๋ฅผ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๋Š” ์ผ์€ (๋”ฐ๋กœ ์‚ฌ์šฉํ•˜๋Š” ์ผ ๋งŒํผ์ด๋‚˜) ํž˜๊ฒจ์šด ์ผ์ž…๋‹ˆ๋‹ค. linked data๋ฅผ ๋‹ค๋ฃฐ ๋•Œ๋งŒํผ์€ Rust๊ฐ€ ์•ผ์†ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ์€ ์‹œ์›ํ•˜๊ฒŒ(?) raw pointer๋ฅผ ์‚ฌ์šฉํ•œ ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค:

#![allow(unused)]
fn main() {
pub struct NodeB<T: Clone>{
  pub data: T,
  pub prev: *mut NodeB<T>,
  pub i: usize,
  pub next: *mut NodeB<T>
}
}

Rc<RefCell<>>์ด ๊ฐ์‹ธ๋˜ prev์™€ next ๋…ธ๋“œ๋ฅผ ์ด์ œ๋Š” raw pointer๊ฐ€ ๊ฐ€๋ฅดํ‚ค๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. raw pointer๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค๋Š” ๊ฒƒ ๋ง๊ณ ๋Š” ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋‚˜ ์ „๋ฐ˜์ ์ธ ์‚ฌ์šฉ์—๋Š” ํฌ๊ฒŒ ๋‹ฌ๋ผ์งˆ ๊ฒƒ์ด ์—†์Šต๋‹ˆ๋‹ค. ์ด์ œ ์ด์›ƒ ๋…ธ๋“œ์— ์ง์ ‘ ์ ‘๊ทผํ•  ๋•Œ๋Š” unsafe ์Šค์ฝ”ํ”„๋ฅผ ์”Œ์–ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค. unsafe ์Šค์ฝ”ํ”„๋ฅผ ์”Œ์–ด์ฃผ๋Š” ๊ฒƒ์ด ๋ฒˆ๊ฑฐ๋กœ์šธ ์ˆ˜๋„ ์žˆ์ง€๋งŒ, Rc์™€ RefCell์˜ ์ด์ค‘๊ตฌ์กฐ๋ฅผ ๋šซ๊ณ  ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•˜๋Š” ์ผ๋ณด๋‹ค๋Š” ํ›จ์”ฌ ๊ฐ„๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

๋‹ค๋งŒ ์ด์ œ unsafe raw pointer๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, memory leak์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ๋” ์‹ ๊ฒฝ ์จ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ์ปจ๋Œ€ ๋”์ด์ƒ ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ๋…ธ๋“œ์˜ ๋ฉ”๋ชจ๋ฆฌ๋Š” ์ง์ ‘ drop์‹œ์ผœ์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์ปจ๋Œ€ ์ค‘๊ฐ„์— ์žˆ๋Š” ๋…ธ๋“œ๋ฅผ ์ž˜๋ผ๋‚ด๋Š” ๋‹ค์Œ ์—ฐ๊ด€ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•œ๋‹ค๊ณ  ํ•ฉ์‹œ๋‹ค:

#![allow(unused)]
fn main() {
fn cut(node: *mut Self) -> Option<(T, *mut Self)> {
  unsafe {
    let prev = (*node).prev;
    let next = (*node).next;
    let data = (*node).data.clone();

    // destruct cut out node
    let _ = Box::from_raw(node);

    (*prev).next = next;
    (*next).prev = prev;

    Some((data, next))
  }
}
}

๋ณด์‹œ๋‹ค์‹œํ”ผ, ์ค‘๊ฐ„์— ์žˆ๋Š” node๋ฅผ ์ž˜๋ผ๋‚ด๊ณ  ์ด์›ƒํ•œ prev์™€ next๋ฅผ ์„œ๋กœ ์ง์ ‘ ์—ฐ๊ฒฐ์‹œ์ผœ์ฃผ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ๋˜๋ฉด ์ž˜๋ ค๋‚˜์˜จ ๊ฐ€์šด๋ฐ node๋Š” ๋‹ค๋ฅธ ๋…ธ๋“œ๋“ค์— ์˜ํ•ด ๋”์ด์ƒ ์ ‘๊ทผ๋  ์ˆ˜ ์—†๋Š”, ๋ถ• ๋œฌ ์ƒํƒœ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. raw pointer๋ฅผ ์‚ฌ์šฉํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ณ„๋‹ค๋ฅธ ์กฐ์น˜๊ฐ€ ์—†์œผ๋ฉด ๊ทธ๋Œ€๋กœ memory leak์ด ๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ let _ = Box::from_raw(node);์™€ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ํ•ด๋‹น raw pointer๋ฅผ ์†Œ์ง„ํ•˜๊ณ , scope ๋ฐ–์—์„œ ์ž๋™์œผ๋กœ drop๋˜๊ฒŒ ํ–ˆ์Šต๋‹ˆ๋‹ค.

์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋‹ค ์‚ฌ์šฉํ•œ ์ดํ›„์—๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋ชจ๋“  node์— ๋Œ€ํ•œ drop์„ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋งŒ์ผ ์ด๋Ÿฐ ์กฐ์น˜๊ฐ€ ์—†์ด miri test๋ฅผ ์‹ค์‹œํ•ด๋ณด๋ฉด memory leak์— ๋Œ€ํ•œ ์ˆ˜๋งŽ์€ ๊ฒฝ๊ณ ๋ฅผ ๋งˆ์ฃผํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ๋†€๋ผ์šด ์‚ฌ์‹ค์ด ํ•˜๋‚˜๋” ์žˆ์Šต๋‹ˆ๋‹ค. ์•ž์„œ safe ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•œ NodeA์˜ ๊ฒฝ์šฐ, miri test๋ฅผ ์‹ค์‹œํ•˜๋ฉด memory leak ๊ฒฝ๊ณ ๊ฐ€ ๋œจ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋‹จ์ˆœํžˆ linked list๊ฐ€ ์•„๋‹Œ circular list๋ฅผ ๋งŒ๋“ค์—ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ์ฒ˜์Œ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋งŒ๋“ค ๋•Œ head์™€ tail์„ ์—ฐ๊ฒฐ์‹œ์ผœ์ฃผ์ง€ ์•Š์œผ๋ฉด miri์˜ ๊ฒฝ๊ณ ๋„ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด ์ ์€ ์•„๋ฌด๋ž˜๋„ circular list๋ฅผ ๋งŒ๋“œ๋Š” ์ด์ƒ ์–ด์ฉ” ์ˆ˜ ์—†์ด ๊ฐ์ˆ˜ํ•ด์•ผ ํ•  ์ ์ธ๋ฐ์š”, ์ด ๋•Œ๋ฌธ์—๋ผ๋„ ์ฐจ๋ผ๋ฆฌ Rc<RefCell<>> ๊ตฌ์กฐ๊ฐ€ ์•„๋‹Œ raw pointer๋ฅผ ์ฃผ์˜๊นŠ๊ฒŒ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ๋‚˜์€ ์„ ํƒ์ง€๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๊ฒ ์ฃ .

Circular linked list๋Š” ์ด๋ ‡๊ฒŒ ๋ฒˆ๊ฑฐ๋กญ๊ณ  ์œ„ํ—˜ํ•˜์ง€๋งŒ... ์—ฌ์ „ํžˆ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ๋ถ„์•ผ๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ ๊ณ„์‚ฐ ๊ธฐํ•˜ํ•™(Computational geometry)์—์„œ ๋‹ค๊ฐํ˜• ํด๋ฆฌ๊ณค์˜ ๊ผญ์ง“์ ์ด๋‚˜ ์„ ๋ถ„ ์ •๋ณด๋ฅผ ํ‘œํ˜„ํ•  ๋•Œ ๋งŽ์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋”ฑ! ๊ฐ์ด ์˜ค์‹œ์ฃ .

Fig1. Circular linked list๋Š” Computational Geometry์—์„œ ๋งŽ์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค

Unsafe ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•œ NodeB๊ฐ€ safe ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•œ NodeA๋ณด๋‹ค ์„ฑ๋Šฅ์€ ์šฐ์ˆ˜ํ• ๊นŒ์š”? ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ ์ƒ์„ฑ, ๋…ธ๋“œ ์‚ญ์ œ, ๋…ธ๋“œ ์ถ”๊ฐ€, ์Šคํ… ์ด๋™, ๋ฐ์ดํ„ฐ ์กฐํšŒ ๋“ฑ ๋ช‡ ๊ฐ€์ง€ ๊ธฐ๋Šฅ์„ ์ƒ์ •ํ•˜๊ณ  bench test๋ฅผ ์ง„ํ–‰ํ•ด๋ณธ ๊ฒฐ๊ณผ ์ „๋ฐ˜์ ์œผ๋กœ 95% ์ •๋„ ๋” ํšจ์œจ์ ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ ๋ฐ์ดํ„ฐ ์กฐํšŒ ์†๋„๋Š” unsafe ์ฝ”๋“œ๊ฐ€ 30~40% ์ •๋„ ๋” ๋นจ๋ž์Šต๋‹ˆ๋‹ค. ๋ฒค์น˜๋งˆํฌ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋„ ์œ„ repository์— ์žˆ์Šต๋‹ˆ๋‹ค.

๋งŒ์ผ ์ด๋Ÿฐ์ €๋Ÿฐ ์ด์œ ๋กœ unsafe ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด Miri๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ข‹๊ฒ ๋„ค์š”! ๐Ÿ™‚

wrapup

Rust๋Š” ์•ˆ์ •์ ์ธ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ์ œ๊ณตํ•˜๋Š” ์ ๋„ ๋งค๋ ฅ์ ์ด์ง€๋งŒ, Nightly ๋ฒ„์ „์„ ํ†ตํ•ด ๋‹ค์–‘ํ•˜๊ณ  ํ†กํ†ก ํŠ€๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ์ ๋„ ๋งค๋ ฅ์ž…๋‹ˆ๋‹ค. Miri ์—ญ์‹œ Rust์˜ ๋งค๋ ฅ ํฌ์ธํŠธ ์ค‘ ํ•˜๋‚˜์ฃ .

๋‹ค๋งŒ Miri๋Š” ์—ฌ์ „ํžˆ ๋ฐœ์ „ ์ค‘์— ์žˆ์œผ๋ฉฐ, ๊ทธ ์ปจ์…‰์ƒ ๋ชจ๋“  ๊ฐ€๋Šฅํ•œ ์˜ค๋ฅ˜๋ฅผ ์žก์•„๋‚ผ ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋งŒ์ผ unsafe ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด Miri ๊ฐ™์€ ์™ธ๋ถ€ ๋„๊ตฌ์—๋งŒ ์˜์กดํ•˜๊ธฐ๋ณด๋‹ค ์ฒ˜์Œ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ ์•ˆ์ „์„ฑ์— ๊ฐ๋ณ„ํ•œ ์ฃผ์˜๋ฅผ ๊ธฐ์šธ์—ฌ์•ผ๊ฒ ์ฃ . ๋„ค, ๋‹น์—ฐํ•œ ์–˜๊ธฐ์ž…๋‹ˆ๋‹ค! ๐Ÿ”ฌ