CodeBlanks Logo Icon CodeBlanks

Drop and cleanup

Learn memory cleanup and the Drop trait in FFI.

Drop and cleanup

C libraries usually offer a "create" and a "free" function. In a Rust wrapper, the freeing goes inside Drop; this way the user can't forget it, and it also runs on errors or panics:

unsafe extern "C" {
    fn malloc(n: usize) -> *mut c_void;
    fn free(p: *mut c_void);
}

struct CTampon {
    p: *mut u8,
    n: usize,
}

impl Drop for CTampon {
    fn drop(&mut self) {
        // SAFETY: p came from malloc and is freed only here.
        unsafe { free(self.p as *mut c_void) };
    }
}

A safe interface

Offering access through a slice without giving out the raw pointer makes the type safe to use:

impl CTampon {
    fn new(n: usize) -> Option<Self> {
        let p = unsafe { malloc(n) } as *mut u8;
        if p.is_null() { None } else { Some(Self { p, n }) }
    }
    fn dilim(&mut self) -> &mut [u8] {
        // SAFETY: p points to a valid region of n bytes.
        unsafe { std::slice::from_raw_parts_mut(self.p, self.n) }
    }
}

If malloc fails it returns a null pointer; expressing this with Option forces the user to check.

Practice C buffer

Write the CTampon type above (new, dilim, Drop); let Drop print serbest bırakıldı after freeing. Allocate a 4-byte buffer, write the values 1 to 4 into the bytes and print their sum.

Starter code

main.rs
use std::ffi::c_void;

fn main() {
    // buraya yaz
}

Sample solution

main.rs
use std::ffi::c_void;

unsafe extern "C" {
    fn malloc(n: usize) -> *mut c_void;
    fn free(p: *mut c_void);
}

struct CTampon {
    p: *mut u8,
    n: usize,
}

impl CTampon {
    fn new(n: usize) -> Option<Self> {
        // SAFETY: malloc herhangi bir boyut için çağrılabilir.
        let p = unsafe { malloc(n) } as *mut u8;
        if p.is_null() {
            None
        } else {
            Some(Self { p, n })
        }
    }
    fn dilim(&mut self) -> &mut [u8] {
        // SAFETY: p, n baytlık geçerli bir bölgeyi gösterir.
        unsafe { std::slice::from_raw_parts_mut(self.p, self.n) }
    }
}

impl Drop for CTampon {
    fn drop(&mut self) {
        // SAFETY: p malloc'tan geldi ve yalnızca burada serbest bırakılır.
        unsafe { free(self.p as *mut c_void) };
        println!("serbest bırakıldı");
    }
}

fn main() {
    let mut t = CTampon::new(4).unwrap();
    for (i, b) in t.dilim().iter_mut().enumerate() {
        *b = i as u8 + 1;
    }
    let toplam: u32 = t.dilim().iter().map(|&b| b as u32).sum();
    println!("{}", toplam);
}

Expected output

10
serbest bırakıldı

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 Why is freeing memory from C with Rust's <code>Box::from_raw</code> wrong?

  • It doesn't compile
  • The memory came from a different allocator; the allocating side must free it Correct answer
  • Box is slow
  • It's correct

Explanation: Rule: whichever side allocated the memory frees it.

2 Free it

Question
impl Drop for CTampon {
    fn drop(&mut self) {
        unsafe { ____(self.p as *mut c_void) };
    }
}
Answer
impl Drop for CTampon {
    fn drop(&mut self) {
        unsafe { free(self.p as *mut c_void) };
    }
}

Explanation: Memory allocated with malloc is freed with C's free function.

3 Step and owner

Term Meaning
malloc Allocates
Drop::drop Calls free
p.is_null() Allocation failed
dilim() Safe access

All lessons in Advanced