[rust-dev] a proposal for break, ret, and cont

Niko Matsakis niko at alum.mit.edu
Wed Feb 29 07:40:30 PST 2012


So, I've been thinking more about supporting "break", "ret", and "cont" 
in all loops (including those implemented in library code).  I 
originally proposed in #1619 a system that supported "break" and "cont" 
based on enums.  But I was thinking that we could get a bit more 
ambitious and support ret and friends without undue difficulty.  In 
addition, we could get better syntactic sugar to boot.  There is nothing 
amazing in this proposal, the main new part is to repurpose the `for` 
loop to support the various bits of syntactic sugar needed to make 
iteration easy to use for the end user.

The basic return values would be:

     enum loop_ctl<T> { lc_break, lc_cont, lc_ret(T) }
     enum may_return<T> { mr_cont, mr_ret(T) }

So you would write something like vec::iter as:

     fn iter<T,R>(v: [T], f: fn(T) -> loop_ctl<R>) -> may_return<R> {
         let i = 0u, n = vec::len(v);
         while i < n {
             alt f(v[i]) {
                 lc_break { break; }
                 lc_ret(v) { ret mr_ret(v); }
                 lc_cont { /* fallthrough */ }
             }
         }
         ret mr_cont;
     }

The main problem now is that if users write:

     vec::iter(v) {|i| ret foo; }

then they will ignore the return value, and so the ret will be lost.

To address this, we can repurpose the `for` loop form (or use a macro), 
so that you can write:

     for i in v { ... }

Here `v` can be any iterable thing, not just vectors.  It's basically 
syntactic sugar for

     alt (v.iter { ...; cont; }) {
         mr_cont { /* fallthrough */ }
         mr_ret(v) { ret v; }
     }

Note that the compiler adds an implicit "continue" command at the end of 
the loop body.  In addition, any `ret`, `break`, or `cont` statements 
which appear inside the loop body are translated into returns out of the 
closure that represents the body.  This should then work fine for nested 
loops as well.

The main concern I can see would be Graydon's concern that writing all 
loops using an `alt` would impair performance or be a pain to use.  I am 
not *so* concerned about it being a pain, a simple macro like:

     #macro[loop]{alt ... {
         lc_break { break; }
         lc_ret(v) { ret mr_ret(v); }
         lc_cont { /* fallthrough */ }
     }}

would cover most cases, I should think.  (I don't actually know our 
macro syntax.) The performance would be worth experimenting with: I 
imagine that inlining will help and I can imagine LLVM-level 
optimizations to convert break/cont into their ideal equivalents, but if 
they don't exist it'd take some effort to write them.

Niko


More information about the Rust-dev mailing list