So I spent this morning thinking about this problem, and I think I may
have a tentative solution.
First, to motivate the problem, here's an example that crashes, assuming
(a) that hashmaps have a "get" function that hands out an alias to a
callback and (b) that strings are unique (notated by ~):
fn f(hashmap[~str,~str] x) {
fn g(hashmap[~str,~str] x, &str s) {
x.remove("foo");
log_err s; // crash; s is no longer live
}
x.insert("foo", "bar");
x.get("foo", bind g(x, _));
}
Now the rules that Graydon mentioned would forbid the implementation of
"get" in such a way as to give rise to this problem, because the closure
provided to "get" would be considered to possibly alias the string "s"
and calling the function "g" would therefore be illegal. But this is
exactly the kind of function we *want* to be able to write. And, as
Marijn pointed out, we are already violating these rules in rustc in
many places.
I was looking at the Cyclone literature for possible solutions and
stumbled upon these insights:
(1) Handing out aliases to local variables and function parameters
(directly, not substructures thereof) is always safe. The calling
function is the only one that can possibly provide access its own
locals, and it's suspended for the duration of the call.
(2) Unique pointers inside data structures can be, in effect, promoted
to local variables by swapping them with a local. The resulting local
variable can then be handed off as an alias safely per point (1).
These two points provide a possible framework for a solution. The basic
proposal is that aliases are only allowed for local variables and
function parameters.
Under this regime, a hash map using unique pointers can implement "get"
safely as follows:
(1) Look up the key and determine the index of the bucket containing the
value.
(2) Swap the value in that bucket temporarily into a local.
(3) Call the closure to receive the value, passing it as an alias. This
is allowed because the value is a local.
(4) Swap the value local into the bucket again.
Since the value is in effect stored on the side (in the "get" function's
stack frame), the crash in the first example doesn't occur.
Now these rules are probably overly restrictive, but I think we can
relax them some. To give a few examples:
(1) Rvalue expressions are essentially local temporaries, so we should
permit the results of rvalue expressions to be passed as aliases.
(2) We can probably permit *fully interior* chains to be passed as
aliases as well, except for vector elements. I can't think of any
situation in this would be unsafe, but I'm less sure about this one.
Thoughts?
Patrick
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev