That said, I think a postfixing sigil would have been much better. It seems an important enough thing to denote that a bit of special syntax is called for, and the more unique the better.
That said, I think a postfixing sigil would have been much better. It seems an important enough thing to denote that a bit of special syntax is called for, and the more unique the better.
var value = (await (await startRequest()).GetBody()).Root;
While the Rust syntax would make this a little more clear, I do not think it is a dealbreaker. var value = startRequest().await.getBody().await.Root; var value = startRequest.await?.getBody().await?.Root; startRequest()
.then(req => req.getBody())
.then(body => { value = body.Root }) var temp1 = await StartRequest();
var temp2 = await temp1.GetBody();
var value = temp2.Root;
which is instantly grokked.- On the one hand, I agree that "interesting" operations really should be on their own lines. I think I'm going to strongly prefer to keep it to one `await` per line for the foreseeable future.
- On the other hand, I know that many programmers will cram a lot of operations into one line if they can, and when that inevitably happens it would be nice for it to be readable.
- And maybe in the long run, it's possible that a big, mature async ecosystem might make a lot of these operations so commonplace that they aren't "interesting" anymore. Maybe in that world we'll be glad to have a syntax that makes it easy to chain things together.
fn main() {
let world = gives_string().split(" ").next();
println!("{:?}", world);
}
fn gives_string() -> String {
String::from("hello world")
}
This will fail because the String is temporary, and we're trying to get a reference to it (via split), and so it would be deallocated at the end of the line, being a use-after-free. This, however, compiles: fn main() {
let world = gives_string();
let world = world.split(" ").next();
println!("{:?}", world);
}
fn gives_string() -> String {
String::from("hello world")
}
We're shadowing 'world', but the underlying String now lives to the end of main, so everything works, no more use-after free.I think before I'd want a good real-world example of where doing the multi-line thing goes wrong before I'd want to make an argument that this is why postfix is better.
Tradeoffs, tradeoffs.
A comparison: in C++, the behavior of auto-extending the lifetime of const references to temporaries (but not non-const references) is considered a wart in the language design. (Really, you should just assign a temporary to a value because the compiler can elide the copy. : https://abseil.io/tips/101 .)
However, I also strongly perfer to give these subexpressions names. That tremendously helps when debugging, logging, or just discussing the code with colleagues. In fact, these subexpressions do exist and they are a certain unit to reason about, especially when the program is not doing what it should. In these cases (and not only these) you really want to give them a canonical name and not only "line 42".
Maybe there should be a syntactical equivalent for named subexpressions.
var value = StartRequest().await.GetBody().await.Root;
Or if you prefer multiline: var value = StartRequest().await.
GetBody().await.
Root;They can still introduce a sigil later, if the .await syntax turns out to be common enough in code that the long keyword hinders readability. This is how it was done with the try!(...) feature, which now uses ? as a sigil. But as the article points out, pure sigils are a scarce resource so .await might well be the best way to go anyway.