Notes from golang dev learning to use Rust.
I started by watching this (protip: 1.25x speed is about normal), and following along in vscode (my usual IDE, lately).
If you're using vscode, you'll want the rust and rust-analyser extensions. At least as of 2020-08-13. rust-analyser is being made more official, so at some point I expect it will be better utilized by the main rust vscode extension.
If you want code formatting like gofmt, that's rustfmt and you'll need to install it manually by running rustup component add rustfmt. Once you do that, vscode will automagically format your code when you save.
In Cargo.toml, add the dependency under the [dependencies] section. If you go to the top of a crate's page on crates.io, there's a block that says, Cargo.toml and has the line you'd need to put into your Cargo.toml. There's even a button to copy it to your clipboard. There're more details on dependencies in Cargo.toml, here.
You can use cargo-edit. Once installed, the command will then be: cargo add <crate>. That simply adds the specified dependency in the Cargo.toml file with the current(?) version of the library.
How does this work compared with gomod? With that there's the proxy that will cache modules, companies can have internal go proxies, modules get cached locally, etc. How does that compare with vendoring in Rust?
I was attempting to create a function that took instances of a type that implemented a specific trait (called a "trait object" in Rust parlance). I did a search for difference between golang interfaces and Rust traits and found this, which was useful.
The key part was the same simple example written in golang then in Rust.
Go:
type Foo interface { bar() }
func call_bar(value Foo) { value.bar() }
type X int;
type Y string;
func (X) bar() {}
func (Y) bar() {}
func main() {
call_bar(X(1))
call_bar(Y("foo"))
}Rust:
trait Foo { fn bar(&self); }
impl Foo for int { fn bar(&self) {} }
impl Foo for String { fn bar(&self) {} }
fn call_bar<T: Foo>(value: T) { value.bar() }
fn main() {
call_bar(1i);
call_bar("foo".to_string());
}(Bonus: I see that in Rust I can implement an interface on a type I didn't define.)
He goes on to talk about another way to do it in Rust, but doesn't include an example:
So far I've only demonstrated Rust having statically dispatched generics, but Rust can opt-in to the dynamic ones like Go (with essentially the same implementation), via trait objects. Notated like
&Foo, which is a borrowed reference to an unknown type that implements theFootrait.
An example in A Quick Look at Trait Objects in Rust lead me to find that the same example using trait objects would be (or in the playground):
trait Foo { fn bar(&self); }
impl Foo for i32 { fn bar(&self) {} }
impl Foo for String { fn bar(&self) {} }
fn call_bar(value: &dyn Foo) { value.bar() }
fn main() {
call_bar(&1i32);
call_bar(&"foo".to_string());
}There's a pretty complete writeup on Rust traits on the official blog, here. It's a good read.
Annoyingly, you cannot split a single Rust module across multiple files like you can with a Golang package.
Found these generic parameters with apostrophes (source):
pub struct EnumeratePixelsMut<'a, P: Pixel + 'a> where
<P as Pixel>::Subpixel: 'a, { /* fields omitted */ }Apparently things like 'a are lifetime annotations. A way to tell the Rust compiler how long-lived a value should be.
References:
- Lifetime Annotation Syntax (though I recommend reading the whole page to better understand what this is doing)
How to use a macro in a sibling module in the same crate. The key is the use of both
#[macro_export]and#[macro_use]. The order also also important themodline decorated with#[macro_use]must be before themodline of the module where you want to use the macro. It was extremely frustrating to learn this.It was this that got me there, once I was able to ask the right question. And be sure to read the comments on the accepted answer.
In one file:
lib.rs:In multiple files:
lib.rs:a.rs:b.rs: