Drop and cleanup
- Rust course
- Advanced
- Lesson 40/81
- 4 min
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
use std::ffi::c_void;
fn main() {
// buraya yaz
}Sample solution
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
impl Drop for CTampon {
fn drop(&mut self) {
unsafe { ____(self.p as *mut c_void) };
}
}
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 |