Hello, I am writing wrappers for a native C API which contains various Create
and Destroy
functions. These "resources" are exposed via i32
handles upon which various methods operate. As a consumer of these APIs, I cannot be sure, but I suspect, that they mutate the state of these "resources." As a newcomer to Rust, I am wondering...
- When should I mark the parameters to a safe function that wraps an unsafe function as
mut
? - Is
RefCell
the only option when a wrapper object retains a reference to an underlying resource?
For more context, please see the below code snippet.
use std::cell::RefCell;
fn main() {
let resource_a = RefCell::new(ResourceA::new());
let resource_b = RefCell::new(ResourceB::new(&resource_a));
resource_a.borrow_mut().do_something(0, 1);
resource_b.borrow_mut().do_something(2, 3, 4);
}
// FFI Functions
pub unsafe extern "system" fn destroy_resource_a(handle_a: i32) {
println!("destroy_resource_a({})", handle_a);
}
pub unsafe extern "system" fn do_something_a(handle_a: i32, x: i32, y: i32) {
println!("do_something_a({}, {}, {})", handle_a, x, y);
}
pub unsafe extern "system" fn initialize_resource_a() -> i32 {
let handle_a = rand::random::<i32>();
println!("initialize_resource_a() -> {}", handle_a);
handle_a
}
pub unsafe extern "system" fn destroy_resource_b(handle_a: i32, handle_b: i32) {
println!("destroy_resource_b({}, {})", handle_a, handle_b);
}
pub unsafe extern "system" fn do_something_b(handle_b: i32, x: i32, y: i32, z: i32) {
println!("do_something_b({}, {}, {}, {})", handle_b, x, y, z);
}
pub unsafe extern "system" fn initialize_resource_b(handle_a: i32) -> i32 {
let handle_b = rand::random::<i32>();
println!("initialize_resource_b({}) -> {}", handle_a, handle_b);
handle_b
}
// Wrappers
struct ResourceA {
handle_a: i32,
}
impl ResourceA {
pub fn do_something(&mut self, x: i32, y: i32) {
unsafe {
do_something_a(self.handle(), x, y)
}
}
pub unsafe fn handle(&self) -> i32 {
self.handle_a
}
pub fn new() -> ResourceA {
let handle_a = unsafe { initialize_resource_a() };
ResourceA {
handle_a
}
}
}
impl Drop for ResourceA {
fn drop(&mut self) {
unsafe {
destroy_resource_a(self.handle_a);
}
}
}
struct ResourceB<'a> {
resource_a: &'a RefCell<ResourceA>,
handle_b: i32,
}
impl<'a> ResourceB<'a> {
pub fn do_something(&mut self, x: i32, y: i32, z: i32) {
unsafe {
do_something_b(self.handle(), x, y, z)
}
}
pub unsafe fn handle(&self) -> i32 {
self.handle_b
}
pub fn new(resource_a: &RefCell<ResourceA>) -> ResourceB {
let handle_a = unsafe { resource_a.borrow().handle() };
let handle_b = unsafe { initialize_resource_b(handle_a) };
ResourceB {
resource_a,
handle_b,
}
}
}
impl<'a> Drop for ResourceB<'a> {
fn drop(&mut self) {
unsafe {
destroy_resource_b(self.resource_a.borrow().handle(), self.handle_b);
}
}
}