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

Josh Haberman jhaberman at gmail.com
Fri Jan 17 10:39:45 PST 2014


Hi Rust experts,

I would love your advice on how to wrap my C library with Rust, and
particularly how to map my library's ownership/mutability semantics
onto idiomatic Rust types, since my library's ownership/mutability
model is a great match for Rust's approach.

My library's objects have a two-phase lifecycle: when first created
they are mutable and are not thread-safe. When the client has set all
properties to their satisfaction, they perform a "freeze" operation,
after which the object is thread-safe and immutable.

Once immutable, they are also reference-counted. These objects also
can have references between them in a possibly-cyclic graph, but the
library handles intra-object cycles internally; the client only need
maintain their own refcount properly and the library will handle the
rest.

Here is a quick example of using my library's C API (the .h file for
this API is here:
https://github.com/haberman/upb/blob/master/upb/def.h)

  // Create a new object.
  upb_msgdef *md = upb_msgdef_new(&md);

  // It is initially mutable, so I can set its properties now.
  upb_msgdef_setfullname(md, "MyMessage", NULL);

  // Now freeze the msgdef; after this it may only be accessed through a
  // const pointer, though there is no way to enforce this in C/C++ except
  // with assert().
  upb_def_freeze(&md, 1, NULL);

  // Now that it is frozen we can take other refs on it.
  // The second parameter to ref/unref is used in debug mode to match
  // refs and unrefs, to make refcounting bugs easier to track down.
  const upb_msgdef *md2 = md;
  upb_msgdef_ref(md2, &md2);

  // This function is thread-safe and allowed on frozen objs.
  const char *fullname = upb_msgdef_fullname(md2);

  // We must release all refs to prevent the object from leaking.
  upb_msgdef_unref(md, &md);
  upb_msgdef_unref(md2, &md2);

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 could just use a little help on how to map this onto Rust's existing
libraries and traits:

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?

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?

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?

Thanks,
Josh


More information about the Rust-dev mailing list