6 ms·
Good Practices for Writing Rust Libraries
- steveklabnik 11y agoA lot of the stuff that interests me is how much this focuses on making things automatic. Use compiler plugins to automatically check your code, use rustfmt to automatically reformat your code, use highfive to automatically greet new contributors, use homu to automatically ensure good CI, use Travis to automatically upload docs, etc. I like things that are automatic. That way, I don't forget about them.
- killercup 11y ago> I like things that are automatic. That way, I don't forget about them. Yes! And that way I don't have to _trust anyone_ (including myself) to remember them. I can't wait for the day where I can safely run clippy on stable and fail my tests when code is formatted incorrectly (according to rustfmt) :)
- Manishearth 11y agoClippy running on stable is quite far off. We've had discussions about stabilizing plugins, but it's not happening soon. Instead, make clippy an optional feature, and compile with `cargo build --features=clippy` on your CI on nightly.
- killercup 11y ago> make clippy an optional feature Yeah, that's what I'm doing but that is also the reason why nightly is an allowed failure on Travis… I have high hopes for compiler plugins after the current internal refactorings (HIR, MIR), though :)
- Manishearth 11y agoHIR/MIR don't really help much, actually. A stable plugin interface would probably be completely different. And clippy uses a _lot_ of internals, so even if a plugin interface stabilizes there's no guarantee clippy will.
- steveklabnik 11y agoMy understanding was that HIR was supposed to _be_ a stable plugin interface.
- Manishearth 11y agoPerhaps. There's still a long way to go though, since you need a stable AST builder or qquote mechanism, among other things. And HIR isn't nearly enough for clippy to work on stable.
- squiguy7 11y agoI agree with this. Having things be automatic and unified is great. If there is a standard that Rust enforces regarding style or lints, then any crate published should be easily read by others interested in contributing. It removes the overhead of having to consider what style the main author used or what lints are in place when compiling it.
- nordsieck 11y agoOne of the things that's had the greatest impact on me is a comment D.J. Bernstein made about writing good software: For more than a decade I have been systematically identifying error-prone programming habits—by reviewing the literature, by analyzing other people’s mistakes, and by analyzing my own mistakes—and redesigning my programming environment to eliminate those habits. http://www.aaronsw.com/weblog/001502 http://www.aaronsw.com/weblog/001502 Given his track record with writing software largely without security defects, this seems like a promising avenue advancement.
- Stratoscope 11y agorustfmt makes me sad. So do the official Rust and Servo style guides. The whitespace style they mandate ("column align all the things!") is wildly impractical. I used to use this exact same style myself, many years ago, but gave it up when I saw how many problems it caused. I'll never go back to this kind of column-aligned formatting. Here's an example from the rustfmt source code: let mut rewrites = try_opt!(subexpr_list.iter() .rev() .map(|e| { rewrite_chain_expr(e, total_span, context, max_width, indent) }) .collect::<Option<Vec<_>>>()); Here's how I would format that code instead: let mut rewrites = try_opt!( subexpr_list .iter() .rev() .map( |e| { rewrite_chain_expr( e, total_span, context, max_width, indent ) }) .collect::<Option<Vec<_>>>() ); Or, perhaps if the variable names in the rewrite_chain_expr() call were a little longer, like this: let mut rewrites = try_opt!( subexpr_list .iter() .rev() .map( |e| { rewrite_chain_expr( e, total_span, context, max_width, indent ) }) .collect::<Option<Vec<_>>>() ); Either way, the difference is that I use indentation everywhere that the rustfmt style uses column alignment. This kind of indentation-based style has numerous advantages over a column-aligned style. I wrote about this at some length previously, so rather than repeat the details, here are my previous comments for anyone who is curious about the rationale for this indentation style: https://news.ycombinator.com/item?id=10206860 https://news.ycombinator.com/item?id=10206860 https://news.ycombinator.com/item?id=9469713 https://news.ycombinator.com/item?id=9469713 Anyway, it isn't the rustfmt style itself that makes me sad. It's the presumption behind these kind of rigid code reformatters. The bottom line seems to be this collection of notions: 1. Consistent code formatting and whitespace style is vitally important. So important that an automated tool is required to insure that there are no variations in formatting. 2. The exact details of how code is formatted don't matter much. Any formatting style is about as good as any other, as long it is 100% consistent within a project (or language, or whatever). A professional programmer should just find out what the standard is and follow it. 3. There is nothing new to be learned in formatting style. The way we've always done it is good enough, and as good as any other. (See #2.) If you have new and different ideas about whitespace, they are unhelpful and unwelcome. Stop bikeshedding! And that is what makes me sad.
- jerf 11y agoThis isn't really specific to Rust, but, while I wouldn't necessarily do all this stuff while working through a tutorial on Rust, the start of a brand new language is a great time to learn how to set up this sort of stuff on your code from the very beginning. By definition, you must not have a lot of pre-existing Rust code. :) Many of these sorts of things are hell to retrofit on to existing code, but if you integrate them into your workflow early, just shy of free over time to use all along. It's "just shy of free" because the tools themselves train you over time so that you get to the point where you start with conforming code, and the tools are just cleaning up occasional slips rather than requiring you to rewrite everything. So, you actually still net out less effort than trying to retrofit later by making fewer mistakes overall in the first place. I really can't recommend this enough when you're greenfielding something. It's so cheap to start out doing the right thing and so very, very hard to fix it later.... (Obviously, you need to tweak a few things during prototype/exploration, like requirements for docs, but a lot of this stuff is still useful right away.)
- merb 11y agoWhat Rust and Go still misses is still the good IDE. On the C/C++ side you could even use Eclipse and IntelliJ or Visual Studio. And still if you don't like an IDE you could use an editor, however on rust / golang you are forced to the editor, which could be aweful if the project is grown / big, at least for me. Good practices are helping, however the best editorconfig could help you if your codepage grown too big and you don't remeber the function of a specific struct/class/whatever.
- jhasse 11y agoWith Atom and Racer you can jump to definitions and code completition is really really good. With atom-build you can also directly trigger a build / run / test. Set up is basically: apm install language-rust racer build build-cargo But you'll have to download Racer and Rust's source code, too.
- whitenoice 11y agoThere is an intellij plugin for golang - https://plugins.jetbrains.com/plugin/5047?pr=idea https://plugins.jetbrains.com/plugin/5047?pr=idea
- killercup 11y agoI don't know about Go, but I've recently seen this on the Rust side(/site): https://www.rust-lang.org/ides.html https://www.rust-lang.org/ides.html
- merb 11y agoI know that but racer is not yet as "ready" as i liked. Currently go is far ahead especially https://github.com/DisposaBoy/GoSublime https://github.com/DisposaBoy/GoSublime However I liked rust more, for what I'm doing.
- deleted 11y ago[deleted]
- tatterdemalion 11y agoThere's also this checklist: http://areweideyet.com/ http://areweideyet.com/
- Keats 11y agoAnyone knows when the compiler plugins will be stable?
- steveklabnik 11y agoWe don't have a timeframe yet, but the internal refactorings we're working on will help with this, and is one of the reasons we're doing it.
- Manishearth 11y agoStill a while to go. We've got some ideas, but nothing concrete yet, and it's hard to do in a backwards compatible form. And even if it stabilizes, there's no guarantee that things like clippy will be able to use it since clippy relies on a _lot_ of internals. If you want to use a plugin like clippy, I'd suggest you make it an optional dependency (see the README) and do `cargo build --features=clippy` on nightly on your CI.
- mook 11y agoRelated, at what point will "use a stable build, not a nightly" be a good practice for libraries? Actually, do people mainly use nightlies or releases for development? (Of libraries and apps, not the toolchain itself, obviously.)
- Manishearth 11y agoIt already is a good practice. Most people seem to use stable. I work on tools so I prefer nightlies (but I use multirust so it's easy to switch). And Servo (which I also work on) uses its own snapshots.
- jzwinck 11y agoWhat about good practice for the actual writing of Rust library code? I know C++ and there we have advice such as not reordering virtual methods, not using STL types in public APIs, not reusing enum values, exception safety, RAII, etc. What about in Rust? What are some guidelines for writing libraries which will give their users maximum convenience and safety? Is there a stable ABI, and if so what do library implementers need to know about it? How about writing C APIs in Rust?
- kibwen 11y ago> not reordering virtual methods, not using STL types in > public APIs, not reusing enum values There's no reason to worry about any of these in Rust. The design of the language is intended to minimize and discourage footguns in general. > exception safety Exception safety is only a concern from within `unsafe` blocks. If you're using `unsafe` blocks at all, 1) really try not to, 2) if you still must use them, read The Rustonomicon first (here's the specific chapter on exception safety: https://doc.rust-lang.org/nightly/nomicon/exception-safety.html https://doc.rust-lang.org/nightly/nomicon/exception-safety.h... ). > What are some guidelines for writing libraries which > will give their users maximum convenience and safety? Not many guidelines spring to mind. The first is still "don't use `unsafe` blocks if you can help it" (the OP gets into this by mentioning the lint that denies unsafe code in your program, which gives your users an easy way to determine your policy on avoiding unsafe code). The second is "don't panic in library code, use the Result type instead". > Is there a stable ABI Nope! Far-future work. > How about writing C APIs in Rust? Now this is something that could use real documentation and advice like you're requesting. Nothing authoritative springs to mind, but here's a recent experience report on the process to get people started: http://www.joshmatthews.net/blog/2015/10/creating-a-c-api-for-a-rust-library/ http://www.joshmatthews.net/blog/2015/10/creating-a-c-api-fo...
- cpeterso 11y agoIt would be nice if the Rust documentation had a centralized list of std and core traits. For example, I see a summary of the std crate's exported types, modules, and macros but no traits. Such a list would be useful for library developers who want to maximize their library's potential for reuse by implementing all relevant core and std traits. https://doc.rust-lang.org/nightly/std/index.html https://doc.rust-lang.org/nightly/std/index.html
- tatterdemalion 11y agoHopefully this will help: The traits defined in std are listed in the modules that they are defined in, just like all the other kinds of types (structs and enums) defined in std. The top level modules of std are themselves linked from the main index of std, of course, and you navigate through them to find the types you care about. The primitive types have links on the main index of std because they otherwise wouldn't appear in the API documentation (since they are primitive and thus aren't defined in any module), but most types are not (e.g. HashSet<T> isn't linked from the main index). The macros have links on the main index of std because they aren't imported using the module system. What you're probably refering to though are the traits re-exported by the std prelude, which are automatically imported into every Rust module. The prelude is described on this page (linked to from the std main index): https://doc.rust-lang.org/nightly/std/prelude/ https://doc.rust-lang.org/nightly/std/prelude/ The prelude mostly imports traits, but it also imports a few other kinds of items as well: some types (like Option<T> and String) and at least one function (drop()). But what traits your type should implement is not really connected with what traits are imported in the prelude.