[rust-dev] wrapping a C library (ownership/mutability questions)

Alex Crichton alex at crichton.co
Fri Jan 17 12:09:01 PST 2014


> Hi Rust experts,

How flattering!

> This seems like a great match for Rust, because an object could be
> created as "mut" and local to a single task, but then "become" non-mut
> and be sharable between tasks once frozen. And the refcounting scheme
> sounds like a great match for the Arc model.

I agree! You may want to think about things a little differently
though. Using something like Arc means that the refcounting is managed
in rust, whereas it sounds like you're already dealing with it in your
library. You just want to write *wrapper* types which perform the
relevant calls to C.

What you'll likely end up having is two types. Both types internally
are just an unsafe C pointer (which is initialized in your library)
which allow interfacing with the underlying C object. You'll have a
Mutable version for mutable methods and then an Immutable version for
the methods which don't mutate (where creating the immutable version
consumes the mutable version in rust code).

I've answered some specific questions below, but if you have any more,
feel free to ask! You can reach out to me as acrichto on IRC, and I
always love to read code :)

> 1. Can I write a "freeze" function in Rust where my mut pointers
> "become" non-mut pointers in a way that the mut pointers are no longer
> accessible?

I think Erick answered this well. Rust allows you to specify whether a
function requests a mutable pointer "&mut self", or an immutable
pointer "&self", but there only way to forbid methods is to just have
a type that doesn't have those methods.

> 2. Is my notion of freezing the same as Rust's "Freeze" trait? Is
> there a pattern I should follow to make my type "fit in" with the
> stdlib better?

Not quite. The rust Freeze trait is a "kind" which basically means
that the compiler will infer it based on the structure of a type. This
kind is the notion that an object cannot be mutated through a &self
pointer (for example Cell is *not* Freeze).

Your object does indeed ascribe to Freeze, but you don't need to worry
about dealing with the Freeze trait itself. Your library already deals
with freezing internally, so when you write Rust bindings the best way
to expose this would be to have separate types for the
mutable/immutable methods. As Erick suggested, creating the immutable
would consume the mutable type. Under the hood it would look like:

pub struct MyMutableBuilder {
    priv ptr: *my_c_type_t
}

pub struct MyImmutableStruct {
    priv ptr: *my_c_type_t
}

> 3. I think I can write a very Arc-like interface to my refcounting.
> Will my individual refcounting wrappers let me take their address so I
> can pass it to the second param of my ref/unref functions?

I'm a little confused by this question because this sounds like you
want to *port* your library to Rust rather than *wrap* your library
with Rust. If you're porting, then I would certainly recommend Arc. If
you're wrapping, then you wouldn't need Arc because you're already
doing that atomic refcounts yourself.

What you'll probably want is something like:

impl Clone for MyImmutableStruct {
    fn clone(&self) -> MyImmutableStruct {
        unsafe { my_c_ref_function(self.ptr); }
        MyImmutableStruct { ptr: self.ptr }
    }
}

impl Drop for MyImmutableStruct {
    fn drop(&mut self) {
        unsafe { my_c_unref_funtion(self.ptr); }
    }
}

Basically, when you clone() your object, it bumps the refcount. You
can then send the clone'd object to another thread. When the objects
go out of scope (get destroyed) they'll deref the refcount, allowing
you to safely clean things up.


More information about the Rust-dev mailing list