Location: Germany
Remote: Yes
Willing to relocate: Sure
Technologies: Mostly Rust currently (I wrote hunter: https://github.com/rabite0/hunter ), but I've also written a lot of Common Lisp, Shell Scripts, Linux administration, Microcontrollers. Haven't done any Web front-end stuff, it's very ugly in general.
Email: [email protected]
Sorry if that wasn't clear, it's been a rather long time since I checked on the Future story. There seem to be more options now than when I looked into it. When I did my research over half a year ago tokio seemed to be the only option. Glad to see that's not the case any more.
Thanks for the link! I knew about strip, which helps a lot, but the other options look promising as well.
I really care about binary size, since for short running programs, or when startup latency is important it can actually be much faster in real time to have less, but slower code. Especially so on a slow HDD like in my laptop. Loading a few MB when it's under heavy load can take a few seconds.
Edit: Wow, LTO makes a huge difference. Almost a 40% smaller in my case AND it's faster. Really excited to try xargo now.
It was the runtime required to actually run the Futures.
Rust's Futures allow for easy chaining, whereas I only implemented a simple callback that runs on completion, but it doesn't allow one to get at the computed value directly. I simply send a "check for updates" event there. Instead the value gets stored in a Mutex and has to be pulled out of that explicitly, although it can stay in the "Async" type, just that it gets stored directly in the struct after it's pulled out of the Mutex. The advantage is you don't have to pay for the locking all the time and the value can stay where it is, so you don't need to juggle it around. You can see it here: https://github.com/rabite0/hunter/blob/master/src/preview.rs...
I started working on an improved version that actually allows for a callback with a reference to the value upon completion and also a callback that moves the value out, but I haven't finished that yet and after seeing this I might not ever, but I probably will, since it's definitely educative.
Ah, yes, that was probably it. I stopped looking into Rust's Futures pretty much as soon as I noticed how heavyweight the required runtime is, which seemed ridiculous to me, since Futures are so simple conceptually. I have to look into this again.
In general, Rust's big binaries are one of the things that puts me off the most. Even dynamic linking often doesn't help much. Then again, if a few MB, which is a rounding error nowadays, is among the worst I can think of, that's actually pretty amazing ;).
How is the cost in terms of binary size nowadays? Last time I checked using Futures meant adding roughly 5MB to the binary. To me at least that is unacceptable, so I just rolled my own stuff on top of rayon's thread pools.
Also, if you use Box<Error> or failure[1]'s Error type you can get around implementing most of the conversion Traits, but you lose the ability to tell which errors can happen by just looking at the type signature and doing exhaustive matching on them.
Yes, you can select multiple files with space and open them all at once using the exec feature (type "!"). When you press tab it will insert "$s", which will then be substituted with either the normal selection, or when multiple files are selected with those multiple files. This works pretty much like in ranger. It doesn't work when you just open them by "entering" them. I'm not sure if that even makes sense since different file types could be selected and it's not clear what the right thing to do would be. Maybe opening them one after another? I'm not actually sure what ranger does in that case.
The most difficult thing.. That's actually hard to say since I started this to learn more about Rust and it's the largest project I've written so far at >8000 loc, so basically everything. :) But specifically, I guess getting the asynchronous stuff working right was the hardest part. hunter loads everything asynchronously and on demand, like the metadata and directory size. This is to speed up loading times. I'm still not sure it's 100% correct. Rust makes it easy to prevent data races, and it's very strict about what you can share across threads and how you do it, but it doesn't prevent race conditions and logic errors, and reasoning about concurrent programs is still hard.
I actually implemented my own Async type instead of using Futures or something pre-made, because as far as I can tell especially futures come with a pretty heavy binary size cost, which in turn takes time to load, making startup slower on a slow HDD. Other than that just getting the design right was pretty hard. Although the basic foundation is pretty solid I think.
Another reason I started this is to see how usable my TUI widgets are and when I first scrolled through a directory listing at light speed I got somewhat addicted. :) I kept those widgets really generic and they're easy to use in the sense that you can for example get a ListView for your type by just implementing a trait for it and they're very composable so that you can easily get a TabView for your ListView and so on. My plan is to put all that in a library eventually.
Error handling is another problem area. When I started I actually didn't make use of any error handling at all, in the sense that I didn't return errors from my functions, instead handling everything by just returning early, or returning empty strings or whatever made sense. This got problematic after I started heavily using mutexes, channels and other things that return errors, since I couldn't use ? and most of the time there is really nothing to do other than returning early with the error (and logging it), or skipping that operation when it happens in a loop.
I then went the other extreme and started returning Results everywhere. The problem is that right now there is just one single enum with all the dozens of possible errors, so it's hard to say what error can actually happen. Another problem is that I had to remove backtraces after implementing the Async type since backtraces can't be shared between threads for some reason and now the log viewer is pretty crippled. It's actually a "FoldView" and you could fold those backtraces in and out to see what went wrong where, now it's pretty much just a simple list. So yeah, splitting those error types is definitely on my todo list. :)