The problem: Accessor functions always have to copy their return
value, so you can't efficiently get at the content of data structures
(except by duplicating the logic needed to access them).

The original solution proposed was to pass the accessor a block and
pass the value to that block by reference. This would cause blocks to
spring up everywhere (with all the indentation and noise that comes
with it) and be extremely un-composable. A while ago I wrote a message
to this list proposing a system whereby functions could return
references. This week, I've finally implemented that. The exact syntax
and semantics are not yet set in stone, so if you see room for
improvement, reply.

Feature 1: Let bindings can now bind by reference.

    let &x = foo.bar;

This does not copy the content of foo.bar, but introduces a local that
references it. The lifetime of this local can not exceed that of foo
(you can't use it after you've overwritten foo). Currently,
by-reference locals can not be assigned to at all, and must always be
initialized right away (this was a complexity tradeoff -- assignment
which replaces the contents of the referred-to value could be
implemented).

You can put destructuring patterns after 'let &', but you can't mix
by-copy and by-reference bindings in a single destructuring pattern.

Feature 2: Return-by-reference.

   fn get<T>(o: option::t<T>) -> &T

Putting an ampersand in front of the return value of a function
indicates that the function returns a reference pointing into one of
its arguments. Alias analysis will base the lifetime of the returned
reference on the lifetime of the argument. It'll also verify, inside
the reference-returning-function, that the returning happens in a safe
way. You can then do things like this:

    let x = get(foo).bar; // Access the reference directly.
    let &y = get(foo); // Or store it in a by-reference argument
    let z = get(foo); // This will cause the referred-to value to be
copied, so you don't need two variants of accessors

If your function takes more than one argument, you have to specify
which argument you are referencing by providing a number after the &

    fn elt<T>(v: [T], n: uint) -> &1 T

This is 1-based, 0 is reserved for later use.

The above assumes the referenced value is immutably rooted in the
argument (the argument is not mutable in a way that might overwrite
the reference). When this is not the case, you have to annotate it
with an !

    fn current_val(c: cell) -> &!content

This will make the resulting reference somewhat less convenient to use
-- as soon as the value of anything that might alias it (currently by
simple type-based alias analysis), it is invalidated, since that might
result in the value being overwritten. Still, for careful use, or for
immediate consumption, such aliases are much more efficient than
making a copy.

One major unimplemented part is returning references to the content of
objs (this is what &0 is reserved for). The opaque nature of object
types makes this somewhat more tricky to check, but I think I've
worked out a reasonably convenient way to do it. When I have that,
I'll be able to make map.get and map.find return aliases (which was
the motivating example for this whole exercise).
_______________________________________________
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to