Discussion
Loading...

Post

Log in
  • About
  • Code of conduct
  • Privacy
  • Users
  • Instances
  • About Bonfire
Jan :rust: :ferris:
Jan :rust: :ferris:
@janriemer@floss.social  ·  activity timestamp 2 weeks ago

PSA: Please don't do this in #Rust:

if let (Ok(foo), Ok(bar)) = (expensive_op(), expensive_op()) {
println!("{foo} {bar}")
}

It can be very inefficient, causing people to wait on your software wasting their time (I've just seen this in a fairly popular open source project with 3k stars)!

For a better, more maintainable and efficient version, see second toot or have a look at the playground for the full examples:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=fa0f06dc81aa0343ecae9a2c6206b52b

1/2

#RustLang #SoftwareEngineering

  • Copy link
  • Flag this post
  • Block
tuxmain
tuxmain
@tuxmain@toot.aquilenet.fr replied  ·  activity timestamp 2 weeks ago

@janriemer It also depends on whether or not you want lazy evaluation. You may want to call the second one even if the first one fails.

  • Copy link
  • Flag this comment
  • Block
Ian Wagner
Ian Wagner
@ianthetechie@fosstodon.org replied  ·  activity timestamp 2 weeks ago

@janriemer I'm actually really surprised that doesn't get optimized into something better, and I had to add some more examples and time logging to your code to satisfy my curiosity, but you are indeed correct!

I assume it has something to do with the semantics of expression evaluation in Rust? e.g. it's specified somewhere that the full expression (tuple creation in this case) *must* complete before the if / binding / match / whatever?

Link to my updated playground: https://play.rust-lang.org/?version=stable&mode=release&edition=2024&gist=b102e3c8928f2c2ce2c3d5aa5d24c6cc

  • Copy link
  • Flag this comment
  • Block
Jan :rust: :ferris:
Jan :rust: :ferris:
@janriemer@floss.social replied  ·  activity timestamp 2 weeks ago

@ianthetechie Thank you for the example playground.

> I assume it has something to do with the semantics of expression evaluation in Rust?

Yes, I'm not familiar with the exact details, but the tuple will definitely be evaluated first.

It can get really complicated, once we go into pointer land, because there are place expressions and value expressions and their difference is very important with regards to pointers:

Ralf Jung has an excellent blog post on it:
https://www.ralfj.de/blog/2024/08/14/places.html

  • Copy link
  • Flag this comment
  • Block
Janne Moren
Janne Moren
@jannem@fosstodon.org replied  ·  activity timestamp 2 weeks ago

@janriemer @ianthetechie
I'm a beginner, but honestly I would expect the expression to always evaluate both ops, and would be counting on both operations to always run. Having Rust short-circuit it feels like a good source of hard to diagnose bugs to me.

A let chain is better if you want short-circuiting, as it makes it explicit. In my very inexperienced opinion.

  • Copy link
  • Flag this comment
  • Block
Rasmus Kaj 🎼🦀
Rasmus Kaj 🎼🦀
@rkaj@mastodon.nu replied  ·  activity timestamp 2 weeks ago

@janriemer
By now, there is a third possibility that I think is both more readable and more efficient than both alternatives (well, the one you prefer will probably be exactly the same in an optimized build).

```rust
if let Ok(num1) = expensive_op() && let Ok(num2) = expensive_op() {
println!("{num1} {num2}")
}
```

(Ignoring the possibility of threads or async).

  • Copy link
  • Flag this comment
  • Block
Jan :rust: :ferris:
Jan :rust: :ferris:
@janriemer@floss.social replied  ·  activity timestamp 2 weeks ago

// the second `expensive_op` will only be executed, if the first has returned `Ok`

if let Ok((foo, bar)) = expensive_op().and_then(|res| Ok((res, expensive_op()?))) {
println!("{foo} {bar}")
}

2/2

  • Copy link
  • Flag this comment
  • Block
Tom
Tom
@tdelmas@mamot.fr replied  ·  activity timestamp 2 weeks ago

@janriemer Well, it depends. If "expensive" mean "using 100% of all CPU", sure.

But if it only uses 100% of one, then that's not the correction solution.

And if "expensive" means "uses a lot of io", well, it depends, if the happy/most common path is both succeeding, then they can be done in parallel if it's faster (maybe just improve the cancellation of one when the other fail).

  • Copy link
  • Flag this comment
  • Block

bonfire.cafe

A space for Bonfire maintainers and contributors to communicate

bonfire.cafe: About · Code of conduct · Privacy · Users · Instances
Bonfire social · 1.0.1-alpha.41 no JS en
Automatic federation enabled
Log in
  • Explore
  • About
  • Members
  • Code of Conduct