11 ms·
Rust Starter Kit 2020
- steveklabnik 6y agoThis is a good list. cargo tree was upstreamed into Cargo, so you don’t even need to install it via a package these days.
- oefrha 6y ago> Media codecs – Various > Not aware of any great encoders... Video, I haven’t used enough to have an opinion on. If you don't mind having a -sys dependency, you can use an FFmpeg binding. There's ffmpeg-next[1] (which is a continuation of the abandoned ffmpeg[2] crate), and a few others I have no experience with. [1] https://crates.io/crates/ffmpeg-next https://crates.io/crates/ffmpeg-next [2] https://crates.io/crates/ffmpeg https://crates.io/crates/ffmpeg
- johnsoft 6y agoI'd suggest once_cell instead of lazy_static. No macros, faster compiles. https://crates.io/crates/once_cell https://crates.io/crates/once_cell
- dathinab 6y agoYes, but it's more than that. once_cell allows you more ways to do lazy initialization. Like by using `once_cell::sync::OnceCell` instead of `once_cell::sync::Lazy` you can determine the init function when trying to access the once cell. For many cases `Lazy` is what you want but for some `OnceCell` makes it just so much easier. And then you also can use the types outside of a "lazy initialized static/global" use case. So generally it's better (by now).
- archseer 6y agoIt's also on track to be available in std: https://github.com/rust-lang/rust/pull/72414 https://github.com/rust-lang/rust/pull/72414 https://github.com/rust-lang/rust/issues/74465 https://github.com/rust-lang/rust/issues/74465
- CJefferson 6y agoI really like this, I wish more languages had this, unlike a list which gives 200 package to do X, with no idea which is best. My only comment is I prefer 'tracing' to 'log' for logging, but log will do fine.
- jpeter 6y agoIs there a list like this for C#?
- dgellow 6y agoAnd for C++? :)
- worble 6y agoC# has a pretty huge standard library (or Microsoft official packages) that cover a lot of things here. Quickly running through the rust list, the things I suppose I would add: Dev Tools: - Analyzers are a great tool for getting compile time errors. I use Rosylnator (https://github.com/JosefPihrt/Roslynator https://github.com/JosefPihrt/Roslynator) and Xunit Analyzers (https://github.com/xunit/xunit.analyzers https://github.com/xunit/xunit.analyzers) on every project, but there a ton of others focussing on performace, security, etc. - Editor Config is also great for maintaining consistent styling in your project (https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/code-style-rule-options https://docs.microsoft.com/en-us/dotnet/fundamentals/code-an...). Again, you can enforce this at build time, and Visual Studio and Rider will show errors in the IDE. Encryption: - Like the author I don't know much about this topic, but for hashing BCrypt is pretty well known and appears well respected (https://github.com/BcryptNet/bcrypt.net https://github.com/BcryptNet/bcrypt.net) Logging: - Microsoft has their own basics for this, but there are also a lot of 3rd party solutions, you can see a maintained list here: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-3.1#third-party-logging-providers https://docs.microsoft.com/en-us/aspnet/core/fundamentals/lo... (I personally use Serilog). Serialization/Deserialization: - Microsoft have just added their own internal JSON serializer to .NET Core, so you should probably use that where you can, but it doesn't have anywhere near the same features that Newtonsoft's Json.NET does, so if you're in need of those then that's still the battle-tested standard (https://github.com/JamesNK/Newtonsoft.Json https://github.com/JamesNK/Newtonsoft.Json). Time: - Personally I've never had a reason to use it over the standard DateTime, but I've heard good things about NodaTime (https://github.com/nodatime/nodatime https://github.com/nodatime/nodatime) Images: - System.Drawing still exists in the standard library for Core, but it's Windows only I believe - ImageSharp is a great library for native image stuff in .NET Core, works across all platforms (https://docs.sixlabors.com/index.html https://docs.sixlabors.com/index.html). There was some hullaballoo a little while back about it's license, but that appears to be resolved now as it's Apache 2. - There's also an ImageMagick library if that's your jam (https://github.com/dlemstra/Magick.NET https://github.com/dlemstra/Magick.NET) Networking: - .NET obviously has it's own httpclient, but it's worth mentioning Polly here (https://github.com/App-vNext/Polly https://github.com/App-vNext/Polly). It allows you to extend the default Http Client with short circuits, automatic retries, exponential backoffs, etc. Databases: - Probably doesn't need saying, but Entity Framework for an all in one migration, entity mapping and database access library (https://github.com/dotnet/efcore https://github.com/dotnet/efcore). Since moving to Core, it's also a lot faster now, although I think it's still lacking a couple of features from the old framework version. - Dapper is focussed on being fast and simple (https://github.com/StackExchange/Dapper https://github.com/StackExchange/Dapper) - NHibernate is also still a really powerful database library, even if I think it's API kinda sucks (https://github.com/nhibernate/fluent-nhibernate https://github.com/nhibernate/fluent-nhibernate)
- riskable 6y agoThis is awesome! I've been developing firmware in Rust for a Hall Effect keyboard (with springless, magnetic separation switches!) I'm developing/inventing (just ordered the first PCBs!). I really wish we had something similar in the embedded Rust ecosystem as a lot of the crates mentioned don't support #![no_std] (declaration you make at the top of your Rust code to indicate that the compiler should not include the standard library). The world of embedded Rust seems to be developing incredibly fast. Especially since there's so much hardware that you can use with it now and more is on the way (especially in the world of RISC-V... Holy crap there's a lot of interesting and exciting hardware coming!). As a result of that I seem to be regularly encountering situations where I try to do something basic but no one has "made a crate for that yet" (that works with #![no_std]). Just the other day I found out just how difficult it is to benchmark something (i.e. grab the current time, do something, get the time again, then subtract the difference... It's really, really complicated! Way moreso than it should be). If someone wanted to make a name for themselves by developing widely-used software now is the time to make your mark! The world of embedded Rust is waiting for you! FYI: I'm still a newbie when it comes to Rust but I was able to develop a universal driver (works with any hardware platform) for working with 74HC4067 and 74HC4051 analog multiplexers in just a few days... https://crates.io/crates/analog-multiplexer https://crates.io/crates/analog-multiplexer
- agumonkey 6y agoEverytime I play with magnets, the first thing that comes to mind is "would be a nice key feel"
- dathinab 6y agoMissing: `async-std` Which provides a interface like the std library but async. (Through tokio copied a lot of this by now). It uses the same execution implementation as `smol` and generally most of the code from it is shared with `smol` through internal dependencies like `async-executor`. Through lie tokio it's a bit bigger then `smol` which affects compiler times.
- deleted 6y ago[deleted]
- ithkuil 6y agoI'm learning rust and tokio vs async-std confuses me: if I have a library that uses tokio (e.g. tonic grpc), can I use a library that uses async-std and mix calls to functions of those two libraries in the same function?
- wokwokwok 6y agoNo. There’s no clean async abstraction later; once you pick a side, you’re stuck with crates that support it. Some popular crates let you pick using flags which one you want when you add them... but, tldr: no. It’s not ideal, but it’s very flexible. Given they weren’t sure exactly what the async runtime should look like, and the strong backwards compatibility promise, they opted for “get something people can use for now”. ...of course, the python packaging story shows this is a stupid idea in the long run, and I believe the plan is to consolidate into a single “std” async runtime eventually. ...but for now, in your situation, you’re basically screwed I’m afraid. Fork the crate and migrate it yourself is probably the only real solution. The downside is that doing that usually unearths dependencies that are also using the wrong runtime, etc. For now: Pick one, stick with it.
- swsieber 6y agoI think smoke is trying to be that abstraction layer? I know it makes it easy to swap out the runtime.
- ZephyrBlu 6y agoComing from Python and having no Rust experience, it's odd to me that these are all external packages and not apart of the standard library. For instance, Python provides HTTP, regex, logging, etc. out of the box. Is this a difference in philosophy between Python and Rust? (Batteries-included vs unix?)
- ekidd 6y agoThere are a couple of key differences between the two ecosystems here which affect the tradeoffs: 1. Rust's cargo tool manages dependencies, it's almost universally used, and it does a good job. It's most similar to a mature Pipenv. 2. The Rust community normally does a good job with semver and the type checker makes it much easier to notice breaking API changes. 3. The Rust standard library has a strict policy of not breaking backwards compatibility. 4. There are tools like cargo-audit, cargo-geiger, cargo-crev and cargo-deny that can be used to enforce various policies across all dependencies. 5. Rust allows building single-file executables, so you don't need to distribute dependencies at runtime. So in practice, people tend to standardize one or two good solutions outside of the standard library, and very occasionally the best ideas will make it into the standard library. It's partly a philosophical difference, but the entire ecosystem of tools works together to support that philosophy.
- raphlinus 6y agoYes, it is a difference in philosophy. Here is a blog post from 3 years ago where the libs team explored making an official set of libs to more closely match a "batteries included" experience: https://blog.rust-lang.org/2017/05/05/libz-blitz.html https://blog.rust-lang.org/2017/05/05/libz-blitz.html This effort did not end up succeeding. Instead, a more organic, informal process seems to be working pretty well. Lists like this seem to be useful, even if none is officially blessed.
- tlamponi 6y agoA few "two cents"/opinions from me, surely not the complete picture. External crates are easy to include and often come also from rust core developers, this does not actually slows one down but allows for much more flexibility. The big benefit over python is that rust does not dictates what you need to use. And with the trait system you can often just plug in another, now maybe better fitting, solution for a specific crate. Further, note that rust is normally shipped to the user compiled to a binary with all rust code statically linked in, while python isn't. Python is shipped often as code, and if the std library would not include that much that'd mean that one had to ship lots of module code along. So, rust would not actually profit from having a big standard library the same way Python does. Having a big standard library makes it also harder to deprecate and remove cruft (<https://lwn.net/Articles/790677/ https://lwn.net/Articles/790677/>), which can get a problem over the long time.
- IshKebab 6y agoFor thread pools I'm pretty sure you can use Rayon. I agree with 99% of this but I can't agree with Crossbeam. I use it basically every time I need multiple threads because you always end up needing `select!()`. As far as I know std doesn't have anything like that which makes its channels rather useless.
- wngr 6y agoWhile I agree with your sentiment, and do the same, `select!` is not more than looping through a couple of `try_recv` calls. So in the spirit of the author, a few lines of boilerplate vs a new dependency.
- Matthias247 6y ago> `select!` is not more than looping through a couple of `try_recv` calls. That's wrong. Any `try_recv` call will not make the thread park until one of the expected action happened. You can implement a busy loop which calls `try_recv()` - but this is by far not as efficient as what `select` does.
- 3PS 6y agoReally awesome list! Some thoughts: 1) For error handling, I think anyhow and thiserror have been gaining popularity for applications and libraries, respectively. There's also eyre, a fork of anyhow with some more options apparently, and color-eyre, which is a variation of eyre for colorful backtraces. Long story short, yeah, there's a lot of crates. 2) I've heard good things about sqlx as an alternative to diesel that lets you write raw type-safe SQL into your code and get it checked at compile time. It has good async support too. 3) I would probably mention async-std as an alternative to tokio.
- mplanchard 6y agoThanks for mentioning SQLx. I’ve been writing a SQLite integration recently using rusqlite, which is great and does the job, but some of the SQLx features, like the checked SQL statements and being able to fetch directly into a struct, look like they are worth checking out.
- shepmaster 6y ago> Long story short, yeah, there's a lot of crates. And I'd add one more to the pile: my [SNAFU] crate. It actually does what the author says they do by hand: > So, I just write the boilerplate and make my errors descriptive enough I don’t need a backtrace. When I want to get fancy I implement the built-in `Error` trait, which used to be kinda useless but is now more helpful. And in another five years it’ll still work just fine. SNAFU is all about creating enums for each error case so that the hierarchy of types tells you exactly what and where the error came from. In my ideal usage, you don't need a stacktrace or even origin line number / file name because each specific combination of error is used only once. The nesting of types within specific contexts gives what I like to call a "semantic backtrace" Example usage: #[derive(Debug, Snafu)] enum Error { #[snafu(display("Unable to read configuration from {}: {}", path.display(), source))] ReadConfiguration { source: io::Error, path: PathBuf }, #[snafu(display("Unable to write configuration to {}: {}", path.display(), source))] WriteConfiguration { source: io::Error, path: PathBuf }, } type Result<T, E = Error> = std::result::Result<T, E>; fn example(path: &Path) -> Result<()> { let data = fs::read_to_string(path).context(ReadConfiguration { path })?; fs::write(data, path).context(WriteConfiguration { path })?; Ok(()) } This creates the `Error` enum and implements `std::error::Error` for it, plus what I call context selectors to easily add on to fallible calls to help group them. Note that both SNAFU errors are caused by an `io::Error`, but the context in which the `io::Error` is created is vastly different. > for applications and libraries, respectively I've yet to understand why people want to draw a line between these two cases. For me, the only difference is that the error type in a library needs to be conscious that it's part of the public API. That's why SNAFU can generate [opaque] error types --- Other bonuses: - Doesn't require heap allocation (but can make of use it) - Works in no-std environments - Comes with extension traits for `Result` and `Option`, and feature flagged ones for `Future` and `Stream` - Supports generic errors (types and lifetimes) - Can create backtraces (either from the backtrace crate or the unstable `Backtrace` type in the standard library) [SNAFU]: https://crates.io/crates/snafu https://crates.io/crates/snafu [opaque]: https://docs.rs/snafu/0.6.9/snafu/guide/opaque/index.html https://docs.rs/snafu/0.6.9/snafu/guide/opaque/index.html
- lumost 6y agoWould be really interested in a solid tree/list/doubly linked list structure. The borrow checker appeared to make the construction of such a structure impossible.
- steveklabnik 6y agoPetgraph is the big graph crate. Linked lists get efficiently implemented with unsafe, but they're just not used by Rust folks a lot because they're a pretty niche data structure.
- lumost 6y agoIt's really hard to determine what parts of comp sci are niche a-priori. Most of my current work involves recursive data structures (trees/graphs/linked lists). I don't mind the callout that you need to use unsafe for recursive structures, but it would be useful to acknowledge that as part of the language design in the rust book. As a beginner you can easily pick up a simple LeeCode problem to learn the syntax and accidentally pick an impossible problem.
- ChrisSD 6y agoFor the beginner there's "Learn Rust With Entirely Too Many Linked Lists"[0]. The main issue I have with them is they tend to optimize poorly on modern hardware even when they theoretically should be the right data structure to use (IMHO of course). [0]: https://rust-unofficial.github.io/too-many-lists/ https://rust-unofficial.github.io/too-many-lists/
- steveklabnik 6y agoYes, it is. It's one of the tough parts of the book, I cannot assume anything about the audience. That includes "they even know about recursive data structures," let alone "they want to write them." In general, it's not possible to enumerate all negatives, but it is for all positives, so I try to say more about what is than what is not. The issue is not graphs, trees, and lists. These are useful! It's implementing them the way that you do in CS 101 that is the issue.
- fluffything 6y ago> [Things I avoid] parking_lot: More compact and efficient implementations of the standard synchronization primitives. [goes on about efficiency...] lol, really? No, like, not at all. Parking lot has 1 killer feature, and 1 killer feature only, and this feature makes using the standard synchronization primitives _almost always_ the wrong choice for almost all applications: DEADLOCK DETECTION That's right, if you ever need a Mutex, chances are that your program might run into a deadlock at some point. Do your future self a huge favor and don't use the standard library's std::Mutex. Instead, use parking_lot::Mutex, turn on deadlock detection at compile-time, and save your future self potentially hours of trying to figure out what your program is doing, why is it deadlocking, and how to fix it. parking_lot::Mutex panics if your program deadlocks, printing out the backtrace of each thread involved in the deadlock. This makes discovering, understanding, and fixing dead-locks a 5 minute issue, that's easily and quickly discovered during development in the minutes after introducing a deadlock bug. I mean, you are completely right that, on top of that, parking_lot synchronization primitives are much much faster than those in the standard library. But that's only the cherry on top. --- FWIW, nice post, I agree with all your judgements there. If you are looking for an async runtime that's smaller than tokio, check out the `smol` crate. For a thread-pool, i either just use rayon's (you can just push tasks onto its thread pool), or the futures's simple thread pool from the futures crate.
- temac 6y agoIf you need deadlock detection, probably your high level design is problematic. But I agree this is extremely often the case, so that's extremely often an interesting and valuable tool.
- fluffything 6y agoAfter chasing a bug for hours just to discover that it is a deadlock, its always easier to realize, in hindsight, that deadlock detection would have been useful. Independently of how perfect your architecture is, the moment you add a Mutex to your app, you are opening the door for that to happen. IMO not worth it.
- Communitivity 6y agoI read through this and have to say I disagree with several of the points. Rather than go through them all one by one, I'll just say that my experience with several of these create differ greatly (tokio in particular), and several of the crates recommended would not be the ones I suggested. The http section also doesn't mention Actix or Tide or Rocket. Then again, I am relatively new to Rust myself (1 year), so take this with a grain of salt.
- csnover 6y agoAs a newer Rust developer, this is a very helpful list—trying to find appropriate crates has really been a crap shoot. One thing I can note from personal experience, though, is that saying `structopt` “is relatively slow to compile and doesn’t produce the tiniest code” is quite an understatement—its binary overhead is 379.6KiB and compile time is over 15 seconds[0]! Parsing args is a tiny part of an application, so giving over so much time and memory is crazy to me. I’ve personally settled on using pico, which doesn’t use proc macros so you don’t get the sugar of a declarative arguments object, but it’s fast to build and almost as small as the lightweight recommendation `argh`. [0] https://github.com/RazrFalcon/pico-args#alternatives https://github.com/RazrFalcon/pico-args#alternatives
- ChrisSD 6y agoWhile structopt may be too much for many applications, I would argue it's not necessarily as overkill as it looks. For many CLI applications, arguments parsing and (relevant) help are their primary UI. It absolutely makes sense for larger applications to put a lot of effort into making the experience as painless as possible. Of course if an application just has a few simple args then it may not be worth it. But for something like, for example, ripgrep[0] you'll want something that produces nice help and context sensitive error messages to help the user find the commands they want. In short, a good UI can be a complex beast even if it's only a text interface. [0]: https://github.com/BurntSushi/ripgrep https://github.com/BurntSushi/ripgrep
- csnover 6y agoI get how important UX is, and I still struggle to fathom what in structopt even generates 20x more machine code and 30x slower compilation versus a simpler argument parser which can do 95% of the job. Someone at some time thought that every line of code had to be there for some reason, but it just doesn’t seem like a reasonable default choice to me for most apps, because it’s a huge penalty just to handle some edge cases that won’t come up most of the time. ripgrep, your example of something demands a complex arguments parsing system, seems to have had problems with unnecessary changes in clap (on which structopt is based) slowing down compilation for no reason[0][1] and currently uses none of its configurable features except suggestions[2]. [0] https://github.com/BurntSushi/ripgrep/commit/c8e755f11f31b6da04329cdc7433747bba70150f https://github.com/BurntSushi/ripgrep/commit/c8e755f11f31b6d... [1] https://github.com/clap-rs/clap/commit/739e7048a50d14023ef9ec20cba3cd844f6d4748#r39115688 https://github.com/clap-rs/clap/commit/739e7048a50d14023ef9e... [2] https://github.com/BurntSushi/ripgrep/blob/def993bad1a9275cdc249f04048e5b2065b79f05/Cargo.toml#L57-L58 https://github.com/BurntSushi/ripgrep/blob/def993bad1a9275cd...
- __leo_m__ 6y agoVery good list. There have recently been similar posts about what crates to choose. Always followed by discussions about some of the choices. In professional environments where besides the product time to market and costs are the key points, we don't want to spend time on this kind of discussions and decisionmaking. Or taking risks using libs with 0.x versions. It really is a pitty that the decision to keep essential stuff out of the standard lib is limiting Rusts usage in professional environments. Developer resoureces are another reason, but this is a chicken egg problem. The standard lib issue can be solved much easier.
- scudd 6y agoNice list. Maybe considering adding clap as another option under the section on command line parsing