

Let's build a browser engine, Part 6: Block layout - mbrubeck
http://limpet.net/mbrubeck/2014/09/17/toy-layout-engine-6-block.html

======
pcwalton
I especially like the use of Rust's pattern matching here:

    
    
        match (width == auto, margin_left == auto, margin_right == auto) {
            (false, false, false) => { ... }
            (false, false, true) => { ... }
            (false, true, false) => { ... }
            (true, _, _) => { ... }
            (false, true, true) => { ... }
        }
    

C-style switch isn't powerful enough to handle this due to the _ wildcards, so
in a language without pattern matching you'd have little choice but to fall
back to a chains of ifs and Boolean operators, which would make the code hard
to read (and CSS layout code is notoriously time-consuming to debug, so
anything that makes code easier to read is extremely valuable in practice).
The compiler checks to make sure all cases are handled too.

This sort of thing has made Servo's layout much easier to understand.

~~~
akavel
In case of just booleans, in C it's possible to use a "trick"/workaround with
flags/bit operators. Although certainly more cumbersome and limited. E.g.:

    
    
      #define WIDTH 4
      #define MARGIN_LEFT 2
      #define MARGIN_RIGHT 1
      ...
      unsigned int flags =
        (width ? WIDTH : 0) |
        (margin_left ? MARGIN_LEFT : 0) |
        (margin_right ? MARGIN_RIGHT : 0);
      switch (flags) {
      case 0: //...
      case MARGIN_RIGHT: //...
      case MARGIN_LEFT: //...
      case WIDTH:                 // fallthrough
       case WIDTH | MARGIN_RIGHT: // fallthrough
       case WIDTH | MARGIN_LEFT:  // fallthrough
       case WIDTH | MARGIN_RIGHT | MARGIN_LEFT:
       //...
      case MARGIN_LEFT | MARGIN_RIGHT: //...
      }
    

Although you probably already knew it, and I totally admit it's much uglier
and "workaround-ish". It's just that after reading "isn't powerful enough
[...]" I felt a compulsory urge to show that It Can Be Done, and couldn't
resist, sorry :)

~~~
userbinator
Using a switch on an encoded value was my first thought too after seeing the
pattern, and I think it's actually quite an elegant way of doing it - forming
an array index from booleans and doing a table lookup with that. It's even
clearer when the cases are ordered as

    
    
        (false, false, false) => { ... }  // 0
        (false, false, true ) => { ... }  // 1
        (false, true , false) => { ... }  // 2
        (false, true , true ) => { ... }  // 3
        (true , _    , _    ) => { ... }  // 4-7 (could be the default case)
    

Here's a possible small improvement that could, depending on the compiler, get
rid of a few branches:

    
    
        #define WIDTH_BIT 2
        #define WIDTH (1<<WIDTH_BIT)
        #define MARGIN_LEFT_BIT 1
        #define MARGIN_LEFT (1<<MARGIN_LEFT_BIT)
        #define MARGIN_RIGHT_BIT 0
        #define MARGIN_RIGHT (1<<MARGIN_RIGHT_BIT)
        ...
        unsigned int flags = !!width << WIDTH_BIT
                           | !!margin_left << MARGIN_LEFT_BIT
                           | !!margin_right << MARGIN_RIGHT_BIT;
        ...
    
    

I'm not too familiar with Rust and Google isn't being helpful, so does anyone
know how the Rust compiler implements pattern matching? Does it do something
like the above or does it resort to if/else conversion?

~~~
akavel
Is it 100% sure that !! gives only 0 or 1? I'd be very grateful for some hard
citations/references for that (across various standards, C89/99/...,
C++98/03/11...), I'd be happy to learn if I can safely rely on that?

~~~
ben0x539
C99: 6.5.3.3 p5: The result of the logical negation operator ! is 0 if the
value of its operand compares unequal to 0, 1 if the value of its operand
compares equal to 0. The result has type int. The expression !E is equivalent
to (0==E).

C++11 (or a near enough draft): 5.3.1 p9: The operand of the logical negation
operator ! is contextually converted to bool (Clause 4); its value is true if
the converted operand is false and false otherwise. The type of the result is
bool.

and 4.7 p4 on integral conversion: If the source type is bool, the value false
is converted to zero and the value true is converted to one.

Both seem to suggest that when you apply !, you end up with something that is
0 or 1 when used as a number, and adding more ! won't get you out of that.

~~~
akavel
Thanks a lot! And still interested in earlier C/C++ standards, I'm much more
likely to encounter them in the wild for long time.

~~~
ben0x539
Ah, sorry. Looks like C++03 has the same wording in the same sections as
C++11, and ANSI C's Unary arithmetic operators section is numbered 3.3.3.3 and
also has the same wording as C99.

~~~
akavel
Great, thanks!

------
dubcanada
This is by far my favorite rust series. Though probably the only one I've
seen, ignoring that it's absolutely amazing.

Really nice job!

