text: How Rust and I met
  I first heard of Rust long ago through stray searches. I was interested because it seemed like a competitor to Go, which was the most recent language I had learned for a job but that I had a low opinion of. My biggest gripe with Go was error handling. But when I read about Rust's error handling, I misunderstood what I was reading, causing me to think it was even more verbose than Go's, so I stopped investigating.
  I think that was before most of my search for a better language which led me through brief dips into several of them but mostly Haskell. So a long time passed - long enough for me to become fairly competent with Haskell.
  Then I heard about Rust again from a friend who held it in high esteem. I did a little more research and found out that it has sugar to cut down on error handling boilerplate. That combined with an ML-inspired type system made it sound to me like Go done right, so I eagerly jumped in.
  Ownership
  So the big unique thing about Rust is the concept of ownership. Every value is owned by the scope it's declared in, and only one scope is allowed to own it. You have to borrow a value to pass it to another scope (like a function call) without that scope taking ownership away from the caller.
  I hear a lot of people saying the borrow checker is draconian and hard to figure out how to satisfy, but I've never had issues with it. I find the ownership rules pretty intuitive. The borrow checker error messages are good and almost all the time, they're preventing bugs.
  I actually think the ownership rules breed good mental habits and especially make it easier to reason about performance, which is very appropriate for a systems language.
  Ownership also enables Rust to not have garbage collection. It's the first language (besides C) I've learned that doesn't have it. Instead, Rust knows at compile time exactly when everything should be freed. You get the safety and ergonomics of automatic memory management, with the performance and simplicity of manual memory management.
  Type system
  The type system is ML-like. It has interface (trait)-based polymorphism, generics, sum types, and tuples, which is most of the requirements for a type system to never get in the way. It also has inference for local variables (but not for statics and function signatures).
  The only big omission is inheritance. Structs can't embed each other flatly. Go shows what I'd hope to see here: a concise way to compose structs without the extra layer of member access. It's really just syntactic sugar on composition that avoids complicating serialization (with composition, fields of the child struct will serialize as a sub-object unless you do special stuff (like delegate the work to third-party library authors so it only works with that library)).
  This is especially problematic for frameworks like the Diesel ORM, where you may need separate copies of each model for querying and inserting (and maybe another for updating since there's no default arguments either!).
  No magic
  Rust has basically no "magic". Syntactic constructs like indexing, iteration, and comparison use traits that you can implement on custom types. Even the implementation of
  Vec, the main container type, is library code - no compiler magic involved.
  There's also the
  derive feature to automatically generate instances of certain traits for structs, if all their members implement them (like
  Eq and
  Ord).
  Error handling
  So error-handling was the #1 thing I hoped Rust would improve over Go (type system was #2). It does, but not as much as I hoped. The improvements are:
  Sugar to cut down on boilerplate. Instead of (Rust without the sugar):
  let val = match could_fail() { Ok(val) => val, Err(err) => return Err(err), }
  We can do:
  let val = could_fail()?;
  Excellent. If
  could_fail()returns an
  Err, we'll just propagate it upward, and otherwise,
  valbecomes the unwrapped success value. Barely any more verbose than an exception language, and more explicit.
  Error-handling is checked at compile time. Since errors are a proper part of the type system via the
  Resultsum type, trying to use a
  Resultvalue as its
  Oktype is a type error. You have to do that pattern match thing or otherwise unwrap it first. (
  .unwrap()is a way of saying "although the type checker can't tell, I'm sure it's
  Okso just give me the unwrapped value and explode at runtime if it turns out to be an
  Err".)
  Composability of functions is maintained because
  Resultonly takes up one return slot (and Rust has tuple indexing anyway).
  The big downside we still have is that there are no stack traces by default. Propagating an error with
  ? only propagates the original error, with no context added, so when you see the error, it won't have a line number or any other accompanying information, let alone a stack trace. You get that out of the box in most dynamic languages, but in Rust you have to work hard to get them.
  r/rust users informed me that the situation is similar to Go in that you're just expected to use third-party crates to get sane error handling. There isn't even just one that's dominant; apparently the verdict is that
  anyhow is appropriate for applications and
  thiserror is appropriate for libraries.
  anyhow works like this:
  use anyhow::{Context, Result}; fn main() -> Result<()> { let args: Vec<String> = std::env::args().collect(); do_stuff(&args[1], &args[2], &args[3]) } fn do_stuff(file1: &str, file2: &str, file3: &str) -> Result<()> { let text = std::fs::read_to_string(file1).context("when reading")?; std::fs::write(file2, text).context("when writing")?; std::fs::remove_file(file3).context("when removing")?; Ok(()) }
  And if I pass a filename that doesn't exist, I'll get output like:
  Error: when reading Caused by: No such file or directory (os error 2)
  This is actually almost exactly how Go's github.com/pkg/errors works: you have to write the context messages yourself, which is tedious and prone to wrong error messages if you copy-paste, and you're still not getting a line number, so in a large codebase it can still be a hassle to track down where in the source that message is from.
  Also like Go, you get backtraces on panics (if you set environment var
  RUST_BACKTRACE), but they include libraries.
  Syntax
  Rust's syntax is pretty verbose. Not just that it's a brace and semicolon language, but types take extra characters: function parameters need
  param: Type, whereas in most other static languages it's just
  param Type or
  Type param, and parameterized types need a pair of angle brackets:
  Vec<u8> is a vector of bytes, instead of
  Vec u8 like it would be with Haskell syntax. This can get very ugly with nested types like
  Box<Vec<Option<Thing>>>.
  The syntax for namespacing is
  :: instead of
  . (except struct fields which still use
  .). A downside of this besides being less ergonomic is that it's precedence is misleading when combined with
  .:
  :: is more visual separation so intuitively it should bind less tightly, but it's actually
  Rust is often littered with "glue" calls like
  .to_string() after string literals to turn them from type
  &str into
  String (the difference between those types is for good reason, but you'd think literals would be able to be interpreted as
  String when necessary, just like numeric literals are agnostic). Not having concise string concatenation also contributes to the verbosity, and you also need explicit
  impl StructName {...} around methods defined on a struct, and
  impl TraitName for StructName {...} for around trait implementations.
  No default values in function args
  An annoyance. It's not as much of a hindrance as with Go though, since you can use Option types to avoid the callers having to specify the default value, so it's only an ergonomic issue.
  Default values in struct fields
  Rust actually does supports default values in struct fields via the Default trait, which is a nice surprise, but it has an extremely verbose syntax:
  #[derive(Default)] struct Options { opt_a: bool, opt_b: bool, opt_c: bool, } fn main() { let options = Options { opt_a: true, ..Default::default() }; }
  Go has a much more streamlined syntax for it (you can just omit default fields) but in return it doesn't support custom defaults, only the "zero values" of the field types. In Rust you can provide your own implementation of the
  default method.
  Variable declarations
  Rust requires
  let on the first use of a variable. A second
  let shadows, which solves the lexical coupling issues other explicit declaration languages have. A
  let mut is required to make a mutable binding; immutability by default is important for the borrowing rules to be practical, and it also breeds a good awareness of where mutations can happen.
  Array operations
  The Vec type (which is the main sequence type) accomodates most common sequence operations out of the box: push, pop, membership test, insert at position, remove at position, sort, reverse, filter, map, comprehension, and a ton of relatively obscure ones. Pretty much the only thing missing is negative index.
  Concurrency
  Rust has OS-level threads as well as async/await. The designers said somewhere that OS-level threads made more sense than green threads because Rust is supposed to be a systems language, and I'm happy with that.
  It's communication system is similar to Go's, but ownership solves data races and mutex hell. Mutexed data can't be accessed without locking because of the type system, and when the unwrapped data goes out of scope, it's automatically relocked. You can still deadlock of course, but this makes mutexes much easier to work with.
  Resource management
  Rust actually leverages ownership to solve this too. Files are automatically closed when they go out of scope; it's handled by the Drop trait which you can implement. I think it's the most elegant solution I've ever seen.
  On the other hand, it is less flexible than the Go and Python solutions in that it doesn't pull double-duty for other use cases. As far as I know, nothing in Rust provides the full power of
  defer or
  finally.
  Module system
  The thing I've found most confusing about Rust is the package import/namespacing system. There's the
  use keyword, which is actually not really the same thing as
  import from other languages. There's
  pub use for re-exporting. There's
  mod, which is a bit confusing because it's used both to explicitly declare a module and to indicate that the definition is in another file, and
  pub mod. I'm apparently not alone in being confused by all this. There's weird stuff like import paths starting with
  :: and the
  crate keyword for the current crate, and the
  extern crate keyword which they say should only be necessary for "sysroot" crates, but I've found it seemingly necessary to work with Diesel.
  I actually love the way
  use works. You don't strictly have to have a
  use declaration at the top to be able to use an external crate because dependencies are all declared in
  Cargo.toml; all
  use does is unwrap namespaces. For example,
  use std::env; lets you use
  env directly in that scope, but without it you can still reference
  env as
  std::env, which can be more convenient for single uses, and I'm very attracted to the idea of not having to edit something at the top of the file when I realize I need to use a stdlib module on line 500.
  In general, I despise Rust's zealous data hiding. Some unsafe code legitimately requires the outside to not mess with certain stuff, but most often defaulting everything to private just means cutting off possible uses for no reason. Library authors can't anticipate everything a caller might have reason to do, so
  pub should be the default.
  Another onerous restriction is that a trait can only be implemented in either the crate that defines the type or the crate that defines the trait. You can't implement external traits on external types. The Rust book explains:
  This rule ensures that other people’s code can’t break your code and vice versa. Without the rule, two crates could implement the same trait for the same type, and Rust wouldn’t know which implementation to use.
  But couldn't the import system fix this? Couldn't you just only import one implementation? At the very least, couldn't it be allowed to make private implementations?
  Another part of the Rust book offers wrapper types as a solution, but also explains how that's inadequate.
  Macros
  Rust uses macros to have type-safe string formatting, JSON literals, and other niceties. I believe they're also how
  serde is implemented. They're a lot better than C macros though; instead of naive string substitution, they create their own syntax contexts.
  I've never extensively used macros in any language, but my impression of them so far is that they're awesome. A much more enlightened solution to all of these problems than runtime reflection in an otherwise static language.
  Tooling
  Rust doesn't have build system hell.
  cargo Just Works (and comes with subcommands to generate the project boilerplate for you).
  Cargo.toml is powerful too; you can specify dependencies by filesystem path, git URL, or
  crates.io name. Most other languages I know make filesystem path imports hard.
  The compiler is the most helpful I've ever seen. It shows source context with colored output, the error descriptions are pretty good, and there's a
  rustc --explain feature that gives a more in-depth explanation of an error. It also automatically points out unused stuff and unhandled Results, which is great!
  There's even an official formatter and linter:
  rustfmt and
  clippy, which can be invoked as
  cargo subcommands for easy awareness of the project, and you can even see a list of all the clippy lints.
  Documentation
  The content of documentation is pretty average, but I think the tool for it is above so.
  cargo doc generates it in HTML, and has the
  --open flag to automatically open it in your browser. The output includes links everywhere, a great layout, and a search bar.
  Stdlib and ecosystem
  Terrible in every way.
  The standard library is tiny. It features absolutely nothing besides language basics and type methods, no randomness, not even a time struct.
  std::time features the
  Duration,
  Instant, and
  SystemTime types; the latter two are completely opaque meaning you can't do things like get the year number out of them (of course there's no
  strftime or
  strptime). Even Haskell has more of a stdlib than this.
  The ecosystem is npm all over again. Every crate seems to have a hundred recursive dependencies. For every task, there are a dozen libaries, half of them are deprecated and the other half have weird APIs and similar names. Usually none are stable.
  Hopefully this will be improved over the next couple of years. But Go is only one year older than Rust and already has a Python-level stdlib and ecosystem.
  Performance
  My impression of Rust's performance is that it's extremely good, but especially on memory use. Not having garbage collection is probably its main edge over other compiled languages. See this page about Rust vs C speed (yes, it's a two-sided comparison!). Basically, Rust is as performant as you can hope to be while offering great protection against memory safety and other bugs that plague every program written in C or C++.
  I think Rust is a great language. I want to see it replace Go. I hope it becomes my go-to. I still have a lot of evaluating to do, but my next big project will definitely be in Rust.
  Most languages are either high or low level, but Rust is both. It's almost as expressive as dynamic languages, but safer than other static languages and has facilities for when you need really fine control over things like memory layout. There's an even a tutorial on writing an operating system in Rust and honestly, that doesn't sound like a terrible idea. Bryan Cantrill also gives the idea a lot of consideration.
  Discussion (12)
  in more general Iterator
  macros but you will have problem regarding borrowing
  you can create a macro in nightly
  The impl is much more powerful with generics
  Thanks for pointing me to the iterator docs. I could've sworn I'd already checked, but somehow I missed those two.
  Could you expand on the last two? I can't find anything on the default_args macro or how to implement it, and on the last one, I'm not sure what that means because languages that don't require the
  implwrapper can still do things like that
  default args playground link
  regarding
  impl
  I would love to see an example of similar behaviour in other languages because I haven't used any other language that much expect Javascript :D
  That default args macro doesn't seem at all satisfactory. The comment it can only be used in one function per module, and it also seems like it only allows one default argument. I don't know proc_macro so I don't know how different that would be.
  Regarding
  impl, I believe this Haskell snippet accomplishes that (implementing methods for a trait bounded generic):
  fixed problems with default args. I am not saying this is a very good macro. I won't even use it myself. Just saying that macros and traits are powerful enough to mimic many things. I would rather use
  Looks like I should learn haskell (couldn't get it to work on windows last time). But atleast to me these look very similar
  Hm, you're right. I could make it in Go but then it wouldn't have generics. I guess I don't really have an example of a statically typed language that does the same thing smaller. Maybe Go 2 will allow it (it's bringing generics, and interfaces in Go are implemented implicitly by implementing all the methods, unlike Haskell and Rust).
  Thank you for the writing Ryan. As someone that is reading the Rust Book I agree with most of your points.
  Concerning the Stdlib and ecosystem:
  I couldn't agree more with you. Anyway, I've found a helpful resource that points to the libraries/crates that are considered "standard" for usual tasks: rust-lang-nursery.github.io/rust-c.... In there, the "Date and Time" section points directly to the use of chrono, for example.
  From what I've seen there were some crates that were incorporated into
  std, but it seems that the community don't always approve this approach (internals.rust-lang.org/t/expansio...).
  I think a list of "featured" libraries or "community picks" in crates.io could be helpful tought 🤔; Because nowadays, for a newcomer, it's really hard to follow the thread to understand that tokio is the "pseudo(?)" "official(?)" async runtime (I guess 😅).
  It's true that many languages
  stdlibraries have become bloated and obsolete. But perhaps the current "rust way" is too extreme.
  Thanks, that Cookbook link is excellent.
  There is a lot I like about rust, but the issues with operator overloading for reference types has nearly put me off entirely. You either have to implement the trait 4 times, making it hard to reason about which is called, use a macro library, or you end up with tons of dereferences at the call site.
  That sounds like something I haven't run into yet
  Found the tracking bug for fixing a number of dereferencing issues github.com/rust-lang/rust/issues/4...
  I cannot say I agree fully with you, but still I appreciate a good article.