Confusing syntax of `.await`

I'd rephrase that as "a good, up-to-date syntax highlighter has no problem", but that's not guaranteed. For example, Github's highlighter of course still gets wonky on let ... else and so I'm looking at a not highlighted return right now.

Plus, code that ends up in our editors is usually the kind we're familiar with, either because we work on it, or because it's written by people whose code we're generally familiar with. But I would argue that reviewing strangers code on the internet is a different beast.

1 Like

Deref hides it, too. Output of that program:

fn main() {
    let foo = Foo::new();
    println!("One");
    println!("{}",foo.bar);
    println!("Two");
}

May never print "Two" (proof), yet you don't complain about that. Why?

There are no “control flow” in await. That's that thing.

Except in exceptional circumstances, but then “regular” field access can do that, too.

Well… in that case you may as well use await in a similar fashion. Or just not use it ? at all, it's just a syntax sugar, after all.

At least position “we should use explicit if after each function call like in Go” is internally consistent. In that case .await would, probably, be a problem (as well as a deref).

It would be interesting to see how many users such language would find, but that would be language which is significantly different from what we have in Rust today where break can even carry value.

If you are planning to do that then developing habits like “? only goes to the end of the line” is bad idea.

Except when you are working in a huge company like Google or Microsoft and may enforce such rules as part of [mandatory] style guide.

Because panic-invisibility is another topic entirely? I'd love to have that more visible as well.

Of course there is? It's a suspension point. Unless we just use different definitions of control flow.

I do that so I spend less time deciphering my own code. I already read more strangers code than my own.

Anyway, since this discussion seems to turn towards hostility as usual, I'll probably just stop.

How is it “a different topic”? .await becomes “a control flow point” if you cancel the future.

It's extremely similar to panic from smart pointer on logical level.

Because .await is extermely similar to smart pointer on logical level: both allow you access to something which may not even be there when “field access” is used, they just use different implementations to achieve that.

As I have already said: this may be even interesting language — but that wouldn't be Rust.

It's a way to take value from the Future. Period.

The fact that it's a suspension point, too, is an implementation detail which should only worry you about as much as panic safety.

It's not about hostility. It's about suitability of .await syntax in Rust.

You have already outlined your position perfectly: you would like an entirely different language where all control flow constructs are explicit and where Deref, ? and await are not special.

I can respect that position. That may be an interesting language (although the one which I, personally, wouldn't use).

But that wouldn't be Rust!

In Rust, given how it's made, .await syntax is not strange or unusual.

Also: if I understand your position right you wouldn't want prefix await either.

Rather I expect that you would want await in the same status as return: only permitted in certain limited places. No?

Okay, I'm coming back, because this is ridiculous.

This kind of behavior is exactly what I'm talking about. Wanting the ability to see where panics happen doesn't make Rust not Rust. I'm not some kind of foreign element to expel just because I'd like to have that.

I'm glad you don't need to care about implementation details like what other code might run during an .await or where panics happen. But I'm no less of a Rust user just because I do.

This is the reason I'm hesitant to even put code on Github. It's the reason I haven't released a single crate, because everytime I get to the point where I could, I think about these encounters and decide it's just not worth it to put my code out there.

3 Likes

Not necessarily, from your code's POV - the runtime can simply loop over the same future until it's ready, without calling any other code in between.

Do you want every function which uses vector indexing or signed arithmetic be annotated as "can panic", too?

Another example of problems in this discussion: Where does this absolute come from? Where did I say anything about every function?

For me, it's simply not clear where the line can be drawn. Which panics should be visible, and which shouldn't? When it's not specified, well, the only things I can do is either err on the side of "every panic possible", or ask you this question, either explicitly or with example.

1 Like

It would be entirely different language. Because in today's Rust you can get panic from so many pretty innocuous places (x += 1 may panic in a debug build, for crying out loud) an attempt to make it explicit would turn Rust into something like Google Wuffs.

And the ability to just silence any unwritten code with todo! is very important facility of Rust, too.

I'm not saying you are “foreign element” for wanting that. Heck, I want that, too!

I just realize, quite acutely, that this would be entirely different language.

It's not that I “don't need to care”. It's just that Rust is designed around the idea that I shouldn't care.

Sure, but that's the point where you would have to “agree to disagree”. Rust is not designed for needs of people who care deeply about where panic may (or may not originate) or want to always see where precisely suspension points are.

You may agree or disagree with how .await fits into the structure of the language, but it's a bit ridiculous to say that Rust shouldn't do .await like it does other things it already have since you disagree with these decisions, too.

They are already there, if you remove them it would be different language!

By your definition, any new Rust release is a different language.

How is any of this productive?

1 Like

In practice the only way to avoid panics and yet stay relevant as production language is dependent typing a-la Google Wuffs.

In that design most “trivial” panics like integer overflow or buffer overflow are simply eliminated at compile-time. And then it becomes practical to explicitly mark the remaining ones.

But that would be entirely different language. Maybe better, maybe worse, but very different.

How is it not productive?

No. New Rust releases build on the existing facilities and look similar to what was before.

Handling of .await is similar to how Rust handles panic and very similar to how it handles Result with ?.

But you just dismiss that similarly and say that we should ignore all that and, maybe, even go and change the existing Rust to match your ideal.

How is that productive? That's a valid way to design a different language, not a way to improve Rust!

We haven't discussed the scope or anything involving any solutions All I'm saying is that I find control flow at the end less readable, and of course I'm immediately put on the offensive for that kind of crime.

I've told you your approach feels extremely exclusionary. I don't know why you don't seem to care.

How? From my very first message:

I agree that in some other language, different from Rust, and with different design goals, in a language where precise implementation details are prominently exposed and where panic doesn't exist .await suffix syntax would be totally unacceptable.

But Rust is different! It does have Deref, it does have ? and it does have break 'label value.

In Rust suffix .await is perfectly natural.

You may not like it (like I don't like how mut works in bindings) but it follows from the design of the whole language pretty naturally.

I don't know how to break down "control flow at the end is less visible than at the front to me". That's all I'm saying. I don't know how to rewrite that to make it clear. Everything else is projection on your part at this point. Finding different ways to be hostile doesn't change that.

Any change technically makes it a new language, and most languages change drastically over their lifespans.

Arbitrary lines dividing "changes" from "a new language" are not particularly helpful, unless you like splitting hairs over remodeled Greek ships full of Scotsmen.

8 Likes

The difference to me is what I call "carried"/"delayable" vs "uncarried"/"immediate" effects. (I don't know whether there's an official name.)

Basically, ? and .await exists because it's fine to do them later. You can call a "try function" outside of a try block, and you can call an "async function" outside of an async block. It's just when -- or if! -- you get around to ?ing or .awaiting them that you need to be in such a context.

But that's fundamentally not the case for const and unsafe. You can't call something then make it const later. You can't call an unsafe fn speculatively, and only use the output sometimes when you can prove that it was safe later.

Thus I don't see the use in both unsafe { … } and !!. If we mark every unsafe operation with !!, then the block isn't actually doing anything, thus isn't needed.

(Though I do think we need a better way to help people ensure they've proved all the safety preconditions for all the unsafe things they're doing. But to me the biggest part of that is when there are multiple things -- like remembering for x.unchecked_div(y) to prove not just the obvious y != 0 but also the easy-to-forget x != MIN || y != -1. And I don't think that !! helps with that problem, so it's not the overall unsafe design change I'd pick.)

This gets to the whole "tootsie pop" thing, though. unsafe blocks don't and basically can't mark the scope of invariant violation, because the whole module matters for soundness.

4 Likes

Most languages are only ever used by a few dozens of people. Rust wasn't designed to be such a language.

Popular languages also change over time, but they usually do that without radically changing approaches.

C# is extremely popular. Today's C# is almost nothing like it once was.

We may call them "versions" instead of giving them new names, but they are nevertheless different languages. It is only a matter of degree.