3 ms·
FTA: let result = reqwest::blocking::get(url); let response = match result { Ok(res) => res, Err(err) => return Err(err), }; Is that truly idiom
by Someone 4y ago
FTA:
let result = reqwest::blocking::get(url);
let response = match result {
Ok(res) => res,
Err(err) => return Err(err),
};
Is that truly idiomatic? I would think the Err(err) => return Err(err) line needlessly constructs a copy of result. Or is that necessary because of the borrow checker?
(Also, for those unfamiliar with it: reqwest is not a typo. See https://github.com/seanmonstar/reqwest https://github.com/seanmonstar/reqwest)
- richardwhiuk 4y agoThe idiomatic way to write this would be: let response = reqwest::blocking::get(url)?; In the way written there's no additional copy.
- Ambroisie 4y agoAs pointed out, the idiomatic way to do this is to use the try operator (`?`). But to answer your question about copies : Rust is a move-only language, copies are actually called `.clone()`, except for a few types which are cheap enough to copy that they implement the `Copy` trait. So in the code you quoted, the match is done on the value of `result` (notice that there is not borrowing/`&` operator). The match arm `Err(err)` moves `err` out of `result` and returns it. Obviously, the compiler will optimize away all those moves, it's as if they did not exist.
- littlestymaar 4y ago> But to answer your question about copies : Rust is a move-only language, copies are actually called `.clone()`, except for a few types which are cheap enough to copy that they implement the `Copy` trait. This is almost true, but not exact. Copy is for everything where cloning is just memcopy, that doesn't means it's necessarily cheap ([42;4_000_000] implements Copy, yet it's not cheap to copy at all…). And moving things sometimes (but not always) means the thing is getting memcopied (or it could use a pointer, depending on the optimizer's m̶o̶o̶d̶ euristics)
- throwaway234232 4y ago> I would think the Err(err) => return Err(err) line needlessly constructs a copy of result It sounds like this is coming from a C++ bias? So please forgive me if this is wrong. Rust, in my experience, favors move semantics first, then copy semantics after. I know in C++, we had implicit copy constructors, with move semantics after with rvalue references, where you need to use `std::move` in a lot of cases. So what helps, in my opinion, is to think of Rust as using `std::move` as a default.
- saghm 4y ago> So what helps, in my opinion, is to think of Rust as using `std::move` as a default Even more than that, with the exception of types that implement `Copy` (e.g. bools, integers, etc.), using after a move won't just silently degrade to a copy, but will cause a compiler error. Copying is required to be explicit for all but the most trivial types.
- masklinn 4y ago> Or is that necessary because of the borrow checker? It’s not because of the borrow checker, it’s because Rust defaults to (destructive) moves, and implicit copies can only be trivial. Any non-trivial copy has to go through an explicit clone() instead.
- jcranmer 4y ago> I would think the Err(err) => return Err(err) line needlessly constructs a copy of result. You would think wrong. In Rust, everything is move by default. To create a copy, you need to explicitly call .clone() on an object (assuming it implements Clone, of course), or you can implement Copy for the type, which will cause .clone() to be called instead of moving the value. (I'm not actually sure where the compiler will generate calls to clone() on Copy types in favor of moving. In practice, you're not going to implement Copy for a type that isn't trivially copyable anyways [using the C++ definition], so any optimizer would easily be able to elide any excessive copy operations.)
- joaonmatos 4y agoCopy types must be trivially copiable bitwise (the clone implementation is returning the deref of the cloned type), so a copy is a move except for the fact that the previous reference is not invalidated. The docs for the trait (core::marker::Copy) are pretty good