5 ms·
Typestates are an excellent way to provide stronger compile time guarantees, and I wish I saw them more in the wild (I have used them myself when appropriate, t
by afranchuk 5y ago
Typestates are an excellent way to provide stronger compile time guarantees, and I wish I saw them more in the wild (I have used them myself when appropriate, though I was unaware of the terminology).
However, the article mentions:
The attentive reader may have noticed that the consumption and conversion of self into other types implies the values are moved (and in some cases, copied) around.
This is untrue; those moves will definitely be optimized away and the typestates will end up being zero cost.
- nextaccountic 5y agoThere's no guarantee that moves will be optimized away. They won't if you compile without the --release flag, for example. The fact that Rust debug builds are unreasonably slower than C debug builds is a pet peeve of mine. And unoptimized moves are a big part of the problem. Or saying otherwise: "optimizing out" simple moves shouldn't be the job of an optimizer that sometimes doesn't run. It should be always done!
- naasking 5y agoWouldn't that inhibit debugging which is the whole point of a debug build?
- nextaccountic 5y agoIt wouldn't. Moves are just memcpy which has no side effects besides copying something to another address. Eliding moves doesn't break debugging, it actually makes it cleaner IMO. Actual operations on data still happen.
- brundolf 5y agoI'm pretty sure it doesn't do this right now even in optimized builds: https://stackoverflow.com/a/38571602/11392896 https://stackoverflow.com/a/38571602/11392896 Of course the language semantics do still leave the door open for this optimization to happen some time in the future
- comex 5y agoLLVM has at least two optimizations that can elide moves in different circumstances, MemCpyOpt and SROA. But it depends on the situation. If you call a function that moves its argument to its return value, that move definitely won't get elided if the function is not inlined. But simple functions are likely to be inlined.
- nextaccountic 5y ago> If you call a function that moves its argument to its return value, that move definitely won't get elided if the function is not inlined. Wow that's worse than I thought. This means that any pub function that does this pattern of taking ownership then giving it back should have at least #[inline] to enable inlining across crates, or maybe even #[inline(always)]. The difference to C to C++ is that in Rust, such pattern is sometimes necessary to work around the borrow checker.
- afranchuk 5y agoThat answer is from 5.5 years ago. Rust was barely 1.0 then. A lot has changed in 5.5 years. See my godbolt link in the other reply for verification that it does optimize the moves away. Besides LLVM improvements, I suspect the moves might be optimized out at the MIR level, though that's just a hunch.
- afranchuk 5y agoI'm sorry, I didn't qualify my statement well. I did mean in release situations when optimizations are enabled. But I'm not sure whether such optimizations are appropriate in debug builds. I suspect there may be cases where that would interfere with debugging. I know one shouldn't necessarily assume certain optimizations will occur, but after all the point of optimizations is to improve generated code while retaining functionality, and the article was stating that the moves will be an issue, which isn't always true (especially not in typical scenarios of using --release code). However for completeness, below is the article's example, where you can see it inlines pretty much everything as you'd expect. But even if you make it so it won't inline functions (e.g. add println to them) it still optimizes the moves away, which is good. https://rust.godbolt.org/z/3s1KcPa7d https://rust.godbolt.org/z/3s1KcPa7d