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 = (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;