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

Alex Crichton alex at crichton.co
Fri Jan 17 17:02:07 PST 2014


>> The problem with this is that it requires that the *owner* of a type
>> have a *mutable slot*, and you cannot prevent owners from declaring
>> their slots as mutable. In the example you gave, you could write "let
>> mut var2 = Box { x: 2 }" and it would compile, there's nothing
>> preventing usage of the "mut" at the owner.
>
> Are you saying (if I may lapse into C++ vocab for a moment) that I can't
> hide the copy constructor? Anyone can copy my non-mut struct into a
> mut struct at any time and I don't have any say in the matter?

I'm not very familiar with what exactly a C++ copy constructor is, but
rust-wise it mostly has to do with ownerships and moves. Let's say you
have one type, Box. This type has &mut and & methods (dictating when
they're call-able). The problem is that &mut is dictated by the
*owner* of the box. So if your clone() method hands out another Box,
then if whomever owns the box decides to declare it as mutable "let
mut box = orig_blox.clone()", then they'll be able to call the '&mut'
methods.

You need to provide some method of forbidding calling &mut methods,
which would probably involve leveraging the type system in one way or
another. One way is to have two types. The other I know of is to have
the shadow type parameters below. You basically need to forbid "&mut
self" from ever being possible. Something like Arc can accomplish this
because the Arc *owns* the data. The arc then decides to never hand
out &mut pointers, only & pointers. This is along the same lines as a
second type for you though, which I think you're right in trying to
avoid.

Using a shadow type parameter is kinda like having the ancient rust
idea of "type state", although I don't know how that worked or how
equivalent it is to shadow type parameters.

>> With something like this you can write code that works over generic
>> boxes (immutable or mutable), but you have a clear separation of what
>> a mutable box can do and what a frozen box can do.
>
> Hmm, this looks promising. Not as nice as mut/non but not as bad as
> two unrelated types. What does the declaration/use of these types
> look like syntactically? Does the user write out Mutable and Frozen?

Using this implementation, it's not super-elegant, but it's perhaps
better than having two types:

let mut x = Box::new();
x.set_x(100);
let x = x.freeze();
let y = x.clone();
do spawn { use(&y) }
use(&x);

The difficult comes up whenever you have to name the type

fn foo(t: Box<Frozen>) {} // only works on frozen boxes
fn bar(t: Box<Mutable>) {} // only works on mutable boxes
fn baz<T>(t: Box<T>) {} // works on any flavor of box

I would probably say that the shadow type parameter route is the
direction that you'd want to go in, but maybe I'm missing something!


More information about the Rust-dev mailing list