On Jul 30, 2011, at 1:58 PM, Patrick Walton wrote:

> Currently, in Rust variables hoist, just as in JavaScript. I would like to 
> propose removing this feature, and having each "let" introduce a new scope.

This matches C++, so that's a plus for anyone coming to Rust from the main 
Mozilla "systems" language.


> Other, weaker but still compelling arguments in my mind:
> 
> This code compiles but is awfully confusing:
> 
> fn main() {
>  x = 3;
>  log_err x;
>  let int x;
> }

FYI, and not bearing on Rust at all IMHO (so feel free to skip), we on Ecma 
TC39 just renewed our agreement, with better joint understanding of the 
rationale, for let and const (and function-in-braced-block) hoisting to start 
of block *and* for use before initialization (the "temporal dead zone") to be 
an error in JS.next (ES6).

JS already has function hoisting, which wins for programming in top-down style, 
maintaining source without having to topologically sort functions, etc. I made 
functions hoist to top of program or outer function body to mimic letrec, way 
back in the day. Given this precedent, we believe 
function-declaration-in-block, which binds a block-local (as let does), should 
hoist to top of block and be initialized on entry to block.

Last fall, we tried to make let (and const) open C++-style implicit scope at 
declaration site, till end of explicit block. But this does not work with 
function hoisting:

  const k = 0;
  function outer(x, y) {
    if (x) {
      inner();
      const k = y;
      function inner() { print(k); }
      inner();
    }
  }
  outer(42);

The first inner() call cannot see k under the let* or C++-based scope rule. If 
this inner call should print 0 then function inner has dynamic scope. If it 
should throw an error then the scope rule is equivalent to hoisting k to top of 
its containing block. Likewise for let.

The reason the dead zone in which use of k before it has been initialized is a 
temporal or dynamic zone, not a lexical or static zone, is shown above: inner's 
declaration is dominated by k's definition, so a lexical dead zone would allow 
it. But then for this approach to differ from temporal dead zone, the first 
call to inner either must get undefined from k (two values for one const), or 
get the outer k (dynamic scope).

Hence hoisting for all binding forms, with a read and write barrier for use 
before set (write barrier for let; const must be initialized in its declaration 
and it's a static error to assign anywhere else). The barriers are potentially 
significant performance penalties for upvars (any directly-in-block, not 
in-nested-function, use cases are probably bugs; we're still arguing about 
whether to make those static errors). We shall see.

If Rust hoists nothing, in particular Rust does not hoist nested fns, then it 
does not face the same predicament.

/be
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to