[Pre-RFC] Elvis/coalesce + ternary operator

I meant this can happen even within a crate. Into is such an extremely vague and general purpose trait. A lot of people use Into in their own crates for their own random reasons. Some of these people might also want to use or on an overlapping set of types.

1 Like

Believe me that there's a difference between “a tree is for decorationg a yard” and “a tree is for chopping down to get wood”, and that difference is not only a moral/fundamental purpose but also a practical purpose. Because of that we cultivate a different kinds of trees instead of e.g. apples that can serve as a yard decoration, bear some fruits, and we can chop them down on requirement.

But what if you works as a painter and opens multiple cans of paint per day with an edgy handle of a brush? Would you search for a better tool instead?

And what argument could be considered as convincing?

Because there is a tendency that each operator in a language has it's own trait, and we breaking it while users expect it to keep forward.

It's really easy to get lost in terminology here e.g. your example of null handling defines a temporary variable and assignment inside of if expression - is that imperative?

Or another example if a { b() } else { c(); } , is a this expression before semicolon imperative? Could if expression be also imperative because of it?

Again, it don't has a good and ergonomic syntax for simple use cases, and it can't do type inference based on From trait.

Or another example if a { b() } else { c(); } , is a this expression before semicolon imperative? Could if expression be also imperative because of it?

if a { b() } else { c(); } has two cases

  • b returns ()
    • the if evaluates to ()
  • b does not return ()
    • compile time error because the two branches of the if evaluate to different types

any expression followed by a semicolon will always evaluate to ().


it can’t do type inference based on From trait.

On the note of type inference, it is very hard to do type inference on such a generic trait, and calling From::from can be rather expensive, so I would like for it to be visible.

3 Likes

I think Rust is not a Swiss army knife: if you are looking for a better tool that does not fit in Rust, why not just look for another tool set that have the tool you need?

For example, I believe procedure macro will let you define your own embedded DSL. You might probably write a subset of Rust with your specific language extension. If this makes you happy, so do most of us and don't forget to let me know your new tool :slight_smile:

1 Like

I fail to see the adequate motivation for either a coalesce or “the” ternary operation.

Could you explicitly spell out how a or b is better than a.or_else(|| b)/a.unwrap_or_else(b) (based on type information)? (FWIW, I was unaware about this “chaining” behavior from Kotlin’s ?:.)

Could you explicitly spell out how c && a or b is better than if c { a } else { b }? Note that Python specifically chose to use a if c else b because ternary operators are a hard sell.

Alongside this I’d like to pose a challenge that could help motivate this pre-RFC. Provide a real-enough example (at a glance it looks like it’s doing a real computation, not using dummy names) in “current rust” and “with this RFC” styles, and argue why the latter is more understandable with local reasoning to a reader than the former. The people against the RFC can then propose a “current rust” formulation that’s another way of attacking the problem, potentially better, and we can incrementally find the “best” representation both with and without this RFC for comparison.

7 Likes

No, it doesn't. I don't know where you think that happens.

But expression by itself could be imperative, since it could change a state as well as it could contain other expressions that could do the same. Return type really don't matter here.

Wouldn't it be inlined or optimised in any different way?

This is exactly what I'm trying do in this thread

I'm doubt that this tool would be better than tool I use currently

Overall, it's very good idea to a start more deep research and provide a better examples. That should be done early, but I've just assumed that not only I understand the problem and some examples and alternative proposals (different from "you can already do that with") would arrive ITT.

So, I start searching for a real world examples.

In destructuring

The return type of the function b does matter, as it makes the difference of compiling, and not compiling.

Who would the compiler figure out what you want? It's not a problem of codegen, it's figuring out what the programmer wants.

Also, the optimizer is not a magic bullet, it can't optimize away everything, and From::from calls can also to be expensive due to allocations, for example String::from(&str) needs to perform an allocation, and therefore is expensive, and the optimizer cannot optimize that out. Finally, relying on an optimizer to get expected behavior is bad, as even slight changes have drastic, often unpredictable, effects.

Could you please, elaborate especially on the note of if let. I think that if let construct is perfect for handling nullable values. Also we have the Try operator ?, which is useful when in functions that return Result or Option. When try blocks land, this will be even easier.

So, I don't see the point you are trying to make.

1 Like

That's right, but that's also not subject of the discussion. My thesis is that return type of () turns a returning expression into imperative statement, since () represents void. And even if void is a value, it's hard to say that it's a result, since it's useless.

It would know a concrete type I want.
Consider the following code:

option or Err(0);

It would be desugared:

match Or::into_option(option) {
    Some(o) => From::from(o),    
    None => Err(0),
}

And then inlined:

match option {
    Some(o) => Ok(o),    
    None => Err(0)
}

Or did you wanted to say that this couldn't happen?

That's good point. I think that some trait bound on Or type parameter would be required to deny values that allocates in from function. Or we can use another trait instead.
That requires further investigation...

Sometimes you need a more straightforward way to handle nullable values: option or value, option or next_option, option or result...

Handling nullable values in if and match expressions is less straightforward than could be with or.
Handling nullable with Option<T> combinators is less straightforward than with if and match.
Handling null as error using ? and try blocks is not straightforward at all.

Minor nit, there are no From impls for Result

Option::unwrap_or

Option::or and Option::or_else

Convert the result to an option with Result::ok and then use one of the above methods.


I don't see why these are so bad.

1 Like

Imagine that you are complete newcomer in Rust: how you would deal with all of that methods?
And from user perspective: what is the advantage of having multiple ways to do the same thing?

When I was a complete newcomer to Rust, I dealt with all of those methods by learning what they do and using them.

And from user perspective: what is the advantage of having multiple ways to do the same thing?

There are advantages and disadvantages to it, and it depends on what you mean by 'same' and 'multiple' and other matters of context. In general, if two peices of code do the same thing, we want them to look the same, but of course that is usually impossible to enforce and there are other considerations to take into account. For instance, if you're chaining method calls, you probably want to keep chaining method calls. If you're writing for loops, then you want to keep writing them.

1 Like

This is becoming a bit of a semantics game. To me, unwrap_or and or_else and so on do different things, just like + and - do different things, so the status quo is not “multiple ways to do the same thing” to me. And even if this feature were added, a newcomer would still need to understand the Option and Result types and what or does with each of them, and they’ll still need to learn that Option/Result have a bunch of other methods to do various things that or won’t.

But this is all nitpicking of nitpicks of nitpicks; no one’s going to change their mind based on posts like this.

The core issue here is that there’s (apparently?) no argument for this sugar other than the fact that it is a sugar, and the bar for sugar is relatively high (as it should be). If you want to persuade people that this is more useful than the dozens of other proposed sugars that have wisely not been tossed into the language, we should probably take a step back and try to come with some compelling examples of code before/after the sugar that’s significantly easier to understand with the sugar (of course, I can’t come up with any myself, which is why I’m not a fan).

9 Likes

Speaking about the syntax choices, && and or in the same expression look weird, IMHO. Especially if && in this context means something different from the normal short-circuiting boolean “and”.

If we’re talking about chaining conditionals, I’d prefer the syntax to be compatible to RFC 2497 (if let and while let chaining). There’s a possible future extension mentioned there for if-let-or expressions, so, in the similar vein, I’d expect the irrefutable “or chains” to look roughly like the following

let Some(x) = first_option
 || Some(x) = second_option
 || x = fallback_value;
3 Likes