Here are the notes I took on approaching writing a project in Rust for the first time, from a Haskeller's perspective.
I didn't have any particular trouble worked with and anticipating the needs of the move semantics of Rust. It's not much unlike the experience of dealing with pure/non-pure code in Haskell; you just have to think ahead of time about what your code is really doing. I think the hullabaloo about "satisfying the borrow checker" is exaggerated.
Installation is pretty much the same as with Stack, via the install page:
curl https://sh.rustup.rs -sSf | sh
And then you add ~/.cargo/bin to PATH.
Like stack init, this sets up the project:
$ cargo init
There's a Cargo.toml file similar to a .cabal file, in there you can
put your dependencies:
[dependencies]
time = "0.1.12"
regex = "0.1.41"
This feels a bit like specifying extra-deps in your stack.yaml
file, and running other cargo commands will update an additional
Cargo.lock file automatically, which is like maintaining your own
personal stackage snapshot (or a cabal freeze) for the project.
I'm not sure whether that's the best idea: stackage snapshots are shared which I think is better - if I want to be sure two projects build together I can just make sure two teams use the same lts version.
They have cargo run which automatically installs and compiles
everything, familiar to stack users:
chris@precision:~/Work/chrisdone/pid1-rust$ cargo run
Updating registry `https://github.com/rust-lang/crates.io-index`
Downloading regex v0.1.80
Downloading time v0.1.40
Downloading memchr v0.1.11
Downloading thread_local v0.2.7
Downloading regex-syntax v0.3.9
Downloading aho-corasick v0.5.3
Downloading utf8-ranges v0.1.3
Downloading libc v0.2.43
Downloading thread-id v2.0.0
Downloading kernel32-sys v0.2.2
Downloading winapi v0.2.8
Downloading winapi-build v0.1.1
Compiling winapi-build v0.1.1
Compiling winapi v0.2.8
Compiling libc v0.2.43
Compiling utf8-ranges v0.1.3
I found a rust-mode for Emacs: https://github.com/rust-lang/rust-mode
It's pretty much just syntax highlighting, but it works fine.
I installed https://github.com/rust-lang/rust-mode#formatting-with-rustfmt
But it errors out when running with a weird error:
chris@precision:~/Work/chrisdone/pid1-rust$ /home/chris/.cargo/bin/rustfmt
error: toolchain 'stable-x86_64-unknown-linux-gnu' does not have the binary `rustfmt`
I had to force installation with
cargo install rustfmt --force
But that still doesn't work from Emacs:
cond: Rustfmt failed, see *rustfmt* buffer for details
So I don't currently have Emacs-integrated auto-formatting. I gave up on it.
$ cargo install cargo-watch
Like stack --file-watch there is cargo-watch with which you can
run e.g.
$ cargo watch --exec 'run'
To compile and run an executable. Differences seems to be that:
- cargo watch looks at the whole directory, not your package's actual source files. So it'll rebuild a lot more often. Emacs, for example, creates hidden backup files in the source dir and cargo watch reacts to this.
- It seems that cargo watch will actually interrupts the program when a change has occurred, which should be nice for developing web servers (auto-restart).
There is a project called rusti,
https://github.com/murarth/rusti
But it requires the nightly rust version. I attempted to build it but failed. However, once that stabilises, it should be a handy addition to a rustacean's tool-belt like GHCi.
I learned that Rust does have a standard library which is linked to, and it also uses libc. But you can forgo these if you want a completely bare executable. [from the FAQ]
Does Rust have a runtime? Not in the typical sense used by languages such as Java, but parts of the Rust standard library can be considered a “runtime”, providing a heap, backtraces, unwinding, and stack guards. There is a small amount of initialization code that runs before the user’s main function. The Rust standard library additionally links to the C standard library, which does similar runtime initialization. Rust code can be compiled without the standard library, in which case the runtime is roughly equivalent to C’s.
Additionally, I found this useful project for making completely static Rust executables using docker and musl:
https://github.com/emk/rust-musl-builder
I ran this script:
$ rust-musl-builder cargo build --release
And now my executable is static:
$ ldd target/x86_64-unknown-linux-musl/release/pid1-rust
not a dynamic executable
Strings in Rust are apparently all ByteStrings that are interpreted as
UTF-8, which means you cannot have e.g. O(1) unicode point
access. That's alright, because text processing is Hard anyway; we'd
probably be better off if the text package worked like that anyway
(encode/decode less often).
In any case it's an improvement over Haskell.
From the FAQ:
How do I do O(1) character access in a String? You cannot. At least not without a firm understanding of what you mean by “character”, and preprocessing the string to find the index of the desired character.
Rust strings are UTF-8 encoded. A single visual character in UTF-8 is not necessarily a single byte as it would be in an ASCII-encoded string. Each byte is called a “code unit” (in UTF-16, code units are 2 bytes; in UTF-32 they are 4 bytes). “Code points” are composed of one or more code units, and combine in “grapheme clusters” which most closely approximate characters.