5 ms·
Nice article. I finally gave Rust a recently. It's really interesting how new languages evolve, and what "deficiencies" they exert. The article for example uses
by xixixao 8y ago
Nice article. I finally gave Rust a recently. It's really interesting how new languages evolve, and what "deficiencies" they exert. The article for example uses closures, but it's currently impossible in stable Rust to accept a closure that itself accepts a closure as an argument (while you can easily rewrite the same pattern with structs). The borrow checker could still do better on suggesting fixes to common problems (otherwise it's actually quite elegant). What struck me while reading this was the use of assert_eq!(expected, actual), as I've mostly seen the other order. Sure enough I checked and the macro does not define the order. That's unfortunate, as testing against "fixed" "expected" outcome is very common, and leads to more friendly testing devx (which in general while supported out of the box isn't great).
On the other hand, Rust's IDE support, built-in linting, is seriously impressive.
- intertextuality 8y ago> That's unfortunate, as testing against "fixed" "expected" outcome is very common, .... so write it in your preferred order then, in your own code? Why is it "unfortunate" that a generic macro isn't concerned with explicit order? It checks that left == right, [0] no more, no less. Instead of that, I'm free to follow my own conventions when writing tests. [1] [0]: https://doc.rust-lang.org/src/core/macros.rs.html#44 https://doc.rust-lang.org/src/core/macros.rs.html#44 [1]: https://git.sr.ht/~andrewzah/korean_numbers/tree/master/tests/test.rs#L176 https://git.sr.ht/~andrewzah/korean_numbers/tree/master/test...
- masklinn 8y agoFixing the ordering can provide for clearer error messages and the like, for instance rather than the error message being "foo is different from bar" you could have "got foo, expected bar". However I don't see how you can enforce it especially in a simple function / macro (I guess that's one point for BDD-style interfaces): the expected value is not necessarily static so it's not like you could require a const / static / literal even if the language allowed for such an annotation.
- twic 8y agoYou can use a form like: assert_that(foo, is_equal_to(bar)); Which makes it sort of clear that foo is the actual value, and bar is the expected. Especially when you also see things like: assert_that(foo, is_not_zero()); Perhaps that's what you mean by 'BDD-style interfaces'.
- masklinn 8y ago> Perhaps that's what you mean by 'BDD-style interfaces'. Yes. It's usually a more OO shape but usually along the lines of `foo.should.be.equal.to(bar)` or somesuch, a "natural" reading makes it pretty likely the parameter is the expected value.
- intertextuality 8y agoYou could always define a macro like "expect_that", or something similar to ruby's expect(obj).to eq, etc. I find assert_eq!() personally to not be confusing since I always follow the same order, even though I come from a Ruby background with rspec and/or cucumber.
- steveklabnik 8y agoWe looked at the order for assert_eq, and couldn’t find any real consensus. Large testing frameworks in a variety of languages use both orderings. So, we decided to not enforce a particular one.
- skybrian 8y agoThat seems like an odd decision? It's like saying there is no consensus for which side of the road to drive on, so let's not pick one. Standardization is arbitrary but useful here.
- steveklabnik 8y agoThe best argument for picking a specific side was the comment below about "expected, actual" vs "these two things are not equal." In the end, we decided that this just wasn't enough of a benefit to justify making an arbitrary choice. If we did choose, a lot of code would have to change, as existing Rust codebases also used both conventions. Rust already forces the user to follow a lot of rules; this one just wasn't deemed worth it.
- coldtea 8y agoHow is it useful, since it has no observable effect? Equals is equals!
- skybrian 8y agoAs many people pointed out, an expected value is not the same as the actual value. This affects error messages, which are important for ergonomics.
- eridius 8y agoThe downside to enforcing an order is the compiler cannot enforce an order so people absolutely will get it wrong and not notice until much later when the test breaks and they get really confused because the message is telling them the expected value is the actual value and vice versa.
- oconnor663 8y ago> it's currently impossible in stable Rust to accept a closure that itself accepts a closure as an argument You can make it work this way, if the second closure doesn't actually close over anything: let apply_twice = |f: fn(u64) -> u64, x| f(f(x)); let plus_3 = |x| x + 3; let x = apply_twice(plus_3, 1); Or this way, even with closed-over variables, using dynamic dispatch: let apply_twice = |f: &dyn Fn(u64) -> u64, x| f(f(x)); let plus_3 = |x| x + 3; let x = apply_twice(&plus_3, 1); I don't know if it's possible to take a generic closure by value, though. > the use of assert_eq!(expected, actual), as I've mostly seen the other order That's interesting, in my head (expected, actual) is the usual order. For example, https://godoc.org/github.com/stretchr/testify/require#Equal https://godoc.org/github.com/stretchr/testify/require#Equal.
- Thaxll 8y agoRust IDE support is not great tbh, there are recent languages that have way better support. I would day it's average at best.
- bluejekyll 8y agoCompared to what? Very few languages compare well to the excellent support out there for Java. IntelliJ’s Rust plugin is probably the best IDE experience with Rust, and now includes macro expansion. Personally, I still use the RLS with VSCode, mostly because I’m still enjoying the VSCode editor’s versatility when working with many different file types.
- gameswithgo 8y agoC#, Java, Go, Kotlin It is true though that top notch IDE experience is rare.
- gameswithgo 8y agoThe IDE support seems impressive at first, but RLS will start to break down and be unusable as projects get a bit large or a bit complex. One challenge, even if/when rls bugs are all sorted out, is that Rust compile times may make it impractical to use with large projects.
- steveklabnik 8y agoIt turns out, these problems are connected, and we're starting work on a second generation of tooling for this, based off of things like Rosyln. See https://ferrous-systems.com/blog/rust-analyzer-2019/ https://ferrous-systems.com/blog/rust-analyzer-2019/, which is already usable for basic things. This also means improving the compiler. From the 2019 roadmap: https://github.com/rust-lang/rfcs/blob/master/text/2657-roadmap-2019.md#compiler https://github.com/rust-lang/rfcs/blob/master/text/2657-road... > This is where the RLS 2.0 effort comes in. The plan is to build a prototype of the new front-end, thus enabling a truly first-class IDE experience. Wherever possible, we aim to share code between this front-end and rustc by extracting libraries, such as Chalk (for traits), Polonius (for the borrow checker), and a new library focusing on name resolution. Eventually, we will merge this new front-end with the remaining bits of back-end from rustc. rust-analyzer is that "RLS 2.0"
- gameswithgo 8y ago>based off of things like Rosyln That is spectacular news! This is a huge project I had assumed it would be a while before anyone could take this on.
- steveklabnik 8y agoIt is certainly a huge project, but that's one nice thing about the "split everything into a ton of crates" philosophy; it enables this kind of thing to be done in a fairly piecemeal way.
- josteink 8y ago> What struck me while reading this was the use of assert_eq!(expected, actual) This order is common in probably 200+ test-frameworks. For me, I’m surprised whenever I see the opposite. > leads to more friendly testing devx IMO being ergonomic, consistent and well documented leads to good developer experience. Things like this matter little.