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
---> truea && b && b
---> truea && b.c()
---> truea
---> falsea + 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.