Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/tomevault-io/skills-registry --skill rust-unsafe명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SOC 직업 분류 기준
SKILL.md 표시 중
| name | rust-unsafe |
| description | | Use when this capability is needed. |
Guidelines for maintaining soundness, preventing Undefined Behavior (UB), and proving safety boundaries.
Every single unsafe block or function MUST have a preceding // SAFETY: comment explaining exactly why it is safe and how invariants are preserved.
let my_vec = vec![1, 2, 3];
let ptr = my_vec.as_ptr();
// SAFETY: `ptr` is guaranteed to be valid and aligned for reading
// up to length `my_vec.len()`, because it points to the vec's heap data.
unsafe {
let first = *ptr;
assert_eq!(first, 1);
}
Unsafe blocks must build a safe abstraction layer. A safe public function must not trigger Undefined Behavior for any possible inputs.
// Bad: Calling this with an out-of-bounds offset is undefined behavior,
// but the function is marked as safe!
pub fn get_value_unchecked(slice: &[i32], offset: usize) -> i32 {
unsafe { *slice.as_ptr().add(offset) }
}
// Good: Keep the function private or mark the function itself as unsafe,
// requiring callers to guarantee the index validity.
pub unsafe fn get_value_unchecked_safe(slice: &[i32], offset: usize) -> i32 {
// SAFETY: Caller must guarantee that `offset` is within bounds
unsafe { *slice.as_ptr().add(offset) }
}
Never violate Rust's aliasing rules (one mutable reference XOR multiple immutable references) even when using raw pointers.
&mut T while other references exist is Undefined Behavior.std::mem::transmute if you can convert pointers or references via as.let mut val = 42;
let r1 = &mut val as *mut i32;
// SAFETY: Exclusive borrow is maintained; no other references exist to `val`
// during the lifetime of the returned mutable reference.
let ref_mut = unsafe { &mut *r1 };
*ref_mut = 100;
When exposing or calling external C functions, always handle null pointers, ensure string slices are null-terminated, and maintain correct layout representations using #[repr(C)].
extern "C" {
fn c_process_data(data: *const u8, len: libc::size_t);
}
pub fn process_data(data: &[u8]) {
// SAFETY: The external function only reads from the pointer for `len` bytes,
// and we pass a valid pointer and matching length from our slice.
unsafe {
c_process_data(data.as_ptr(), data.len() as libc::size_t);
}
}
Use MaybeUninit for uninitialized memory instead of mem::zeroed():
use std::mem::MaybeUninit;
pub struct RingBuffer<T> {
buffer: Box<[MaybeUninit<T>]>,
head: usize,
tail: usize,
capacity: usize,
}
impl<T> RingBuffer<T> {
pub fn with_capacity(cap: usize) -> Self {
let mut buffer = Vec::with_capacity(cap);
// SAFETY: We only access initialized elements; rest are MaybeUninit.
unsafe { buffer.set_len(cap); }
Self {
buffer: buffer.into_boxed_slice(),
head: 0,
tail: 0,
capacity: cap,
}
}
pub fn push(&mut self, value: T) -> Result<(), T> {
if self.len() == self.capacity {
return Err(value); // buffer full
}
self.buffer[self.tail] = MaybeUninit::new(value);
self.tail = (self.tail + 1) % self.capacity;
(())
}
(& ) <T> {
.head == .tail {
;
}
= { .buffer[.head].() };
.head = (.head + ) % .capacity;
(value)
}
}
<T> <T> {
(& ) {
(_) = .() {}
}
}
A minimal unsafe-backed Vec showing key safety invariants:
use std::alloc::{alloc, dealloc, handle_alloc_error, Layout};
use std::ptr::NonNull;
pub struct RawVec<T> {
ptr: NonNull<T>,
cap: usize,
}
impl<T> RawVec<T> {
pub fn with_capacity(cap: usize) -> Self {
if cap == 0 {
return Self { ptr: NonNull::dangling(), cap: 0 };
}
let layout = Layout::array::<T>(cap).unwrap();
// SAFETY: layout is guaranteed to be non-zero by the cap check.
let ptr = unsafe { alloc(layout) as *mut T };
let ptr = NonNull::new(ptr).unwrap_or_else(|| handle_alloc_error(layout));
Self { ptr, cap }
}
/// # Safety
/// Caller must ensure the memory is valid for T at the returned pointer.
pub unsafe fn ptr(&self) -> *mut T { self.ptr.as_ptr() }
pub (&) { .cap }
}
<T> <T> {
(& ) {
.cap > {
= Layout::array::<T>(.cap).();
{ (.ptr.() * , layout); }
}
}
}
use std::pin::Pin;
use std::marker::PhantomPinned;
pub struct SelfReferential {
data: i32,
ptr: *const i32, // points to `self.data`
_pin: PhantomPinned,
}
impl SelfReferential {
pub fn new(data: i32) -> Pin<Box<Self>> {
let mut boxed = Box::new(Self {
data,
ptr: std::ptr::null(),
_pin: PhantomPinned,
});
// SAFETY: Box ensures the value won't move after pinning.
let ptr = &boxed.data as *const i32;
boxed.ptr = ptr;
Pin::new(boxed)
}
pub fn get_data(self: Pin<&Self>) -> i32 {
// SAFETY: ptr is known to be valid and initialized from new().
unsafe { *self.ptr }
}
}
Try to avoid transmute. Prefer safer alternatives:
// Bad: opaque, no compile-time size check.
let bytes: [u8; 4] = std::mem::transmute(1234u32);
// Good: explicit with FromBytes / Pod traits (bytemuck).
let bytes: [u8; 4] = 1234u32.to_ne_bytes();
// Or for safe reinterpretation of slices:
let bytes: &[u8] = bytemuck::cast_slice(&[1.0f32, 2.0f32]);
#[repr(C)]
pub union IntOrFloat {
pub int: i32,
pub float: f32,
}
// SAFETY: reading the union requires knowing which field was last written.
// Store a discriminant alongside the union to track this.
pub struct TypedValue {
value: IntOrFloat,
is_int: bool,
}
impl TypedValue {
pub fn new_int(val: i32) -> Self {
Self { value: IntOrFloat { int: val }, is_int: true }
}
pub fn as_int(&self) -> Option<i32> {
if self.is_int {
// SAFETY: discriminant guarantees this field was last written.
Some(unsafe { self.value.int })
} else {
None
}
}
}
Always validate unsafe code using Miri, Rust's Undefined Behavior interpreter.
# Run tests inside Miri to catch invalid pointer accesses, alignments, and resource leaks
cargo miri test
# Test with specific flags
cargo miri test -- test_unsafe
#[cfg(miri)]
#[test]
fn test_ring_buffer_miri() {
let mut buf = RingBuffer::with_capacity(4);
buf.push(1).unwrap();
buf.push(2).unwrap();
assert_eq!(buf.pop(), Some(1));
assert_eq!(buf.pop(), Some(2));
assert_eq!(buf.pop(), None);
}
// SAFETY: comments at each unsafe block.Raw pointers can be null, dangling, unaligned, or aliased. Convert to references only after proving validity.
pub fn get(slice: &[u8], index: usize) -> Option<u8> {
if index < slice.len() {
// SAFETY: index is bounds-checked and pointer comes from a valid slice.
Some(unsafe { *slice.as_ptr().add(index) })
} else {
None
}
}
#[repr(C)] for C-facing structs.extern "C" boundaries.extern "C" for callbacks passed to C.catch_unwind to prevent panics from crossing FFI boundaries.use std::panic::catch_unwind;
/// # Safety
/// `data` must be a valid, non-null pointer to a C string.
pub extern "C" fn callback(data: *const libc::c_char) -> i32 {
let result = catch_unwind(|| {
let c_str = unsafe { std::ffi::CStr::from_ptr(data) };
let s = c_str.to_str().unwrap_or("");
process(s)
});
match result {
Ok(val) => val,
Err(_) => -1, // don't let the panic cross FFI
}
}
Unsafe traits mean implementors must uphold invariants the compiler cannot verify.
/// # Safety
/// Implementors must guarantee the returned pointer is valid for `len` bytes.
pub unsafe trait ByteSource {
fn ptr(&self) -> *const u8;
fn len(&self) -> usize;
}
Manually implementing Send or Sync is a major soundness claim. Check interior mutability, aliasing, thread-affinity, and foreign handles.
struct MyRc<T> {
inner: *mut Inner<T>,
}
// SAFETY: MyRc has exclusive access semantics for mutation.
unsafe impl<T: Send> Send for MyRc<T> {}
unsafe impl<T: Sync> Sync for MyRc<T> {}
// Bad: transmute when safe alternatives exist.
let val: u64 = transmute(float_val);
// Good: use to_bits / from_bits for primitive transmutes.
let val: u64 = float_val.to_bits();
// Bad: creating &mut T from raw pointers while aliases exist.
let ptr = &mut val as *mut i32;
let r1 = unsafe { &mut *ptr };
let r2 = unsafe { &mut *ptr }; // UB! Two mutable references to the same value
// Bad: safe functions with unchecked pointer offsets.
pub fn read_at(slice: &[u8], offset: usize) -> u8 {
unsafe { *slice.as_ptr().add(offset) } // no bounds check
}
// Good: validate before unsafe.
pub fn read_at(slice: &[u8], offset: usize) -> Option<u8> {
if offset < slice.len() {
Some(unsafe { *slice.as_ptr().add(offset) })
} else {
None
}
}
unsafe fn.mem::zeroed() where MaybeUninit should be used.For unsafe Rust, require invariants, safety comments, safe-boundary proof, Miri coverage, FFI ownership docs, and justification for each unsafe operation.
// SAFETY: comments.Source: adxptived/Rust-Skills — distributed by TomeVault.