Can I rely on `System.dealloc` calling `free`?

System's GlobalAlloc implementation currently calls free in its dealloc method. Am I allowed to rely on this fact?

The documentation on System says that "this implementation may include extra work" and that I'm not allowed to mix calls with the system allocator, but that is clearly not true for dealloc, so I'm a bit unsure here?


In particular, I'm developing a library that interfaces with a C library, which expects callers to release objects with free, and would like to avoid a dependency on libc.

As an example, let's assume a C library like the following.

// foo.h
typedef struct {
    int some_field;
} Foo;

Foo* get_foo();

// foo.c
#include <stdlib.h>
#include "foo.h"

Foo* get_foo() {
    Foo* foo = malloc(sizeof(Foo));
    foo->some_field = 42;
    return foo;
}

Is the following Rust interface sound?

use std::alloc::{System, GlobalAlloc, Layout};

mod ffi {
    use std::ffi::c_int;

    #[repr(C)]
    pub struct Foo {
        some_field: c_int,
    }

    #[link(name = "foo")]
    extern "C" {
        pub fn foo_get() -> *mut Foo;
    }
}

pub struct FooWrapper(*mut ffi::Foo);

impl FooWrapper {
    pub fn new() -> Self {
        let ptr = unsafe { ffi::foo_get() };
        assert!(!ptr.is_null());
        Self(ptr)
    }
}

impl Drop for FooWrapper {
    fn drop(&mut self) {
        let layout = Layout::new::<ffi::Foo>();
        unsafe { System.dealloc(self.0.cast(), layout) };
    }
}

If you don't want to use the libc crate, you could just call the C function directly by putting this declaration in your own crate:

extern "C" {
    pub fn free(p: *mut c_void);
}

Hmm, how does that interact with linking? Will I need #[link(name = "c")]-like attributes, or do you think it's fine to assume that std (or the library I'm developing against) will link it for me?

If you want to rely on it, you should instead use something like libc_alloc — embedded dev in Rust // Lib.rs to explicitly have the allocator be just malloc+free.

2 Likes