Zig: Proposal: New labeled break syntax, removing colon prefix by reusing `=>` token,

Created on 17 Apr 2020  路  13Comments  路  Source: ziglang/zig

New syntax, overview:

break;                // normal
break => value;       // when returning a value from the current block
break label;          // when breaking a labeled block
break label => value; // when returning a value from labeled block
// break :label;      <-  the colon prefix would not be necessary anymore.

Pros:

  • Get rid of colon prefix. reuse => instead
  • More intuitive and easier to remember (at least my opinion)
  • blk: { ... } or outer: while(... stays as before
  • Simple change to grammar/syntax only. No change to semantics.
  • Would be more consistent with switch(cond) { true => 5, else => 10,};

Cons:

  • Two more characters to type. In my opinion a non-issue.

Example usage, taken from examples in the language reference:

fn rangeHasNumber(begin: usize, end: usize, number: usize) bool {
    var i = begin;
    return while (i < end) : (i += 1) {
        if (i == number) {
            break => true;
        }
    } else false;
}


test "nested break" {
    outer: while (true) {
        while (true) {
            break outer; // no '=>', so outer has to be a label
        }
    }
    // ...
}

test "labeled break from labeled block expression" {
  var y: i32 = 123;
  const x = blk: {
      y += 1;
      break blk => y; // imagine blk with same syntax highlighting color as `blk:` here
  };
}
proposal

Most helpful comment

More intuitive and easier to remember (at least my opinion)

I personally have to think "where does the colon go again?" pretty much every time I write a labeled break.

All 13 comments

More intuitive and easier to remember (at least my opinion)

I personally have to think "where does the colon go again?" pretty much every time I write a labeled break.

Food for thought: Odin's original idea for returning a value from a control construct was to use give:

const val = {
    give 32;
};
assert(val == 32);
const val = blk: {
    if (true) {
         give blk 32;
    }
};

Certainly though, the :blk always seemed awkward to me too.

Food for thought: Odin's original idea for returning a value from a control construct was to use give

Right now only loop control has an anonymous label, then why not assign an anonymous label to assignment as well?

const x = blk_x: if (...) {
    ... // a few lines of code
    break => option_a;
} else {
    ...  // many lines of code
    break blk_x => option_b;
};

const y = blk_y: {
    ...
    const tmp = while (...) {
        ...
        if (...) break => loop_result;
        ...
    };
    ...  // many lines of code
    break blk_y => result;
};

@iology I'm not sure I follow.

@Tetralux No problem. I shouldn't say the assignment is labeled anonymously. :P

I have just found out that break can already work with any labeled blocks {...} so what I really want is:

  1. support outer_label: for/while/if/switch (...)

  2. add an anonymous label to the blocks binded to an assignment:

    const x = (anonymous label:) {
        ...
    };
    
    const x = (anonymous label:) if {
        ...
    } else {
        ...
    };
    

Sorry for the confusion!

I have no idea whether this is feasible in zig, but I would suggest considering how Rust does this: the presence or absence of a semi-colon after the last expression in a block determines whether the block returns the value of the expression or not.

@donaldcallen That was how Zig worked originally, but it was changed because it made it hard to tell the difference between a block returning the result of an if/while/for expression and a block with an if/while/for statement at the end. Here's the original issue.

I see there are some (-1) reactions to this issue, but no explanations yet as to why it might be better to stick with status quo.

How about we just make the colon consistent?

const x = blk: {
    y += 1;
    break blk: y;
}
// or
const x = :blk {
    y += 1;
    break :blk y;
}

How about we just make the colon consistent?

Colon after the label would look strange when not breaking with a value:

blk: {
    y += 1;
    break blk:;
}

and colon before the label would look a bit strange for e.g. labeled loops:

:outer while (true) {
    while (true) {
        break :outer;
    }
}

Edit: kindly ignore this. You could also require label names to start with a special sigil to gain conciseness and ditch the colon entirely:

````zig
break; // normal
break value; // when returning a value from the current block
break #label; // when breaking a labeled block
break #label value; // when returning a value from labeled block
break #; // maybe

label { ... break #label ... } // no colon required to label a block

{ ... break # [val]... } // allowed for conciseness (binds to innermost #)

````

How about we just make the colon consistent?

const x = blk: {
    y += 1;
    break blk: y;
}
// or
const x = :blk {
    y += 1;
    break :blk y;
}

I sort of think of blk: and break :blk that there is something in between those two :'s, and that it thus makes sense to at the declaration of the block, have it after the block name, and when you break out, have it before the block name. I think that it feels quite symmetric this way, even though it may be unintuitive at first. But mayhaps better documentation on where the : goes at which place would help quite a bit for newer people.

But mayhaps better documentation on where the : goes at which place would help quite a bit for newer people.

Worth noting that I fairly often get it the wrong way around, despite not being new to Zig.


Perhaps we can just remove the colon from the break usage?

const val = blk: {
    break blk 31;
};

This cannot be confused with breaking with a single value, because there's two values given to break, plus, you presumably couldn't have another variable called blk, because of the shadowing rules.

Further, this also makes things more consistent, since, as an illustration:

const Int = @IntType(32, false);

const i: Int = 40; // We don't have to specify `@` anywhere here, despite using `Int`!
                   // The name has been bound, and that's that.
const val = blk: {

    break blk 42; // We don't have to specify `:` anywhere here, despite using `blk`!
                  // The name has been bound, and that's that.
}

So there's symmetry to be gained here.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

komuw picture komuw  路  3Comments

daurnimator picture daurnimator  路  3Comments

bheads picture bheads  路  3Comments

S0urc3C0de picture S0urc3C0de  路  3Comments

jorangreef picture jorangreef  路  3Comments