[Pre-RFC] Elvis/coalesce + ternary operator

I'd say that I don't want either of them, because operator overloading is not a best way to go here. It would be surprising to lazily evaluate | and it wouldn't make sense to not evaluate it lazily because we already can use opt.or({ something() }) which is not that bad.

There is a blog post which describes all difficulties behind || and && overloading. I don't think that it makes sense to introduce something like that, especially when final result wouldn't better than or.

The problem is rather similar terminology. There is a lot of functions that exists on both Option and Result, some of them have similar signatures and some not, also there are functions that are specific for the concrete type. It's really hard to use all of that effectively, and proposed or operator could solve this problem at least partially.

Actually, this would work for Result type. On LHS of or we only need to explicitly turn it into Option by using .ok() function call to indicate that Err variant was dropped. The .ok() function as well might be moved into Try trait.

On RHS of or it should work by default e.g. option or result is a valid code that evaluates into Result.

Take a look at desugaring (below in this post), that might explain everything.
A very similar principle to ? operator is used behind (by utilizing From trait).

If I understand this correctly, or would have right to left associativity, and it would take precedence weaker than && and || operators.

  • a && b or c ---> ((a && b) or c)
  • a && b && c or d ---> (((a && b) && c) or d)
  • a || b && c or d ---> ((a || (b && c)) or d)
  • a && b or c && d or e ---> ((a && b) or ((c && d) or e))
  • a || b && c or d || e && f or g ---> ((a || (b && c)) or (d || (e && f)) or g))

The trick here would be to have a property on EXPR which indicates that && operator is inside and we should desugar it as a ternary when or will be present on the RHS.

  • a && b ---> true
  • a && b && b ---> true
  • a && b.c() ---> true
  • a ---> false
  • a + b ---> false

The last inner && separated expression would be a ternary "success" value, and the rest would be a ternary "condition", and value on RHS of or would be a ternary "fallback" value.

  • a && b or c ---> if a { From::from(b) } else { c }
  • a && b && c or d ---> if a && b { From::from(c) } else { d }
  • a && b.c() or d ---> if a { From::from(b.c()) } else { d }

In a case when && is missing, desugaring would be a bit different

  • a or b ---> match Or::into_option(a) { Some(o) => From::from(o), None => b }
  • a + b or c ---> match Or::into_option(a + b) { Some(o) => From::from(o), None => b }

I hope this explains everything under hood.

This could be considered as alternative to the common ternary operator because it can't be implemented in Rust due to syntax constraints, but I don't think that this could be considered as alternative to ternary operator from my proposal:

  • It looks ugly when something like if a { b } else { c } is not written in multiline style.
  • It can't infer return type and we must write boilerplate like if a { Some(b) } else { None }
  • There is else if construct which makes it more cumbersome as it should be
  • It feels more like imperative control flow operator

Just compare:

let x = if a { b } else { c };
let x = a && b or c;

let y = if a { 
    Some(b) 
} else { 
    None 
};
let y = a && b or None;

let z = if a { 
    b 
} else if c { 
    d 
} else { 
    f 
};
let z = a && b 
    or c && d 
    or f;

(ocaml syntax highlighting was applied for this example)

It seems too implicit to me because there's nothing that indicates an Option type. In my proposal there is or None part, which makes it explicit.

This could be better, but there's nothing to indicate that default value would be evaluated, and we must know which value is default. My proposal doesn't have such drawbacks.