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 ๊ฐ์ ์ธ๋ถ ๋๊ตฌ์๋ง ์์กดํ๊ธฐ๋ณด๋ค ์ฒ์๋ถํฐ ๋ฐ์ดํฐ ์์ ์ฑ์ ๊ฐ๋ณํ ์ฃผ์๋ฅผ ๊ธฐ์ธ์ฌ์ผ๊ฒ ์ฃ . ๋ค, ๋น์ฐํ ์๊ธฐ์ ๋๋ค! ๐ฌ