In #Rustlang you can easily use local packages. Specify a path in Cargo.toml:
[dependencies]
some_crate = { path = "/dir/of/some/crate/" }
The path can be relative. If it's a subdir, it will make a workspace: http://doc.crates.io/manifest.html#the-workspace-section (great for monorepos).
You can also use { git = "URL" }
http://doc.crates.io/specifying-dependencies.html#specifying-dependencies-from-git-repositories
A file path in #Rust can be a simple UTF-8 string (String/&str), or a string in an OS-dependent encoding (OsString/OsStr), or a wrapper type specifically for manipulating paths (PathBuf/&Path).
With the help of `AsRef` trait you can write functions that seamlessly work with all of these types:
fn takes_path<P: AsRef<Path>>(path: P) {
let path = path.as_ref();
// Now the path is always just &Path
}
Try it: https://is.gd/01ZiqK
For using the `?` operator, the easiest (laziest) function return type is `Result<…, Box<Error>>`.
Almost all #Rustlang errors can be converted to `std::error::Error`, so you can handle many types of errors without having to worry about converting them.
fn foo() -> Result<(), Box<Error>> {
File::create("file")?.write_all(b"hello")?;
if true {
Err("even strings are convertible to boxed errors")?;
}
Ok(())
}
Examples: https://is.gd/8yeq0z
cargo install cargo-edit
and you'll be able to add dependencies with just
cargo add <crate-name>
When you create a #Rustlang function expecting an optional reference, use `Option<&Foo>` rather than `&Option<Foo>`.
fn optional(good: Option<&Foo>, bad: &Option<Foo>) {…}
Both optimize to the same representation, but `Option<&Foo>` is easier to work with, and is more universal, because it can be cheaply created from an owned option with `.as_ref()`.
State of Rust survey: https://goo.gl/forms/pjcm3r2UjZhpAk4M2
^ please fill in, even if you're not using Rust!
When you define an enum, add
#[derive(PartialEq, Eq)]
enum Foo {…}
to make it work with `==`, e.g.
if foo_value == Foo::Variant {…
Iterate two things together at once:
iter1.zip(iter2).map(|(i1,i2)|…)
for (i1,i2) in iter1.zip(iter2) {…}
To iterate more you can nest zip():
for ((i1,i2),i3) in iter1.zip(iter2).zip(iter3) {}
or better, use Itertools::multizip.
iter.zip example: https://is.gd/5Xq53J
Iteration with `.iter()` gives references. If you want the elements by value, then you need to dereference or clone them, or use `.into_iter()` instead.
vec.iter().map(|x| /* x is a reference (a pointer) */)
vec.iter().map(|&x| /* x is a value */)
vec.iter().cloned().map(|x| /* x is a value */)
BTW: in closure arguments `&` may seem backwards, because that's a pattern, not a type. The syntax for args is |pattern:type|.
See how it works with `for … in`: https://is.gd/qo9fVj
A weird case of function doing nothing doing something:
https://bluss.github.io/rust/fun/2015/10/11/stuff-the-identity-function-does/
This is the permalink for the full archive of #Rust tips: https://octodon.social/@rust
In #Rustlang `pub fn new()` is used as a constructor. Use the `Self` alias to avoid typing the full type name three times:
impl LongComplexTypeName<Annoying> {
pub fn new() -> Self {
Self {
}
}
}
Note that `Self` (uppercase `S`) is a type name, as opposed to `self` variable shorthand used in methods.
Try it: https://is.gd/8gfXQX
When to use `ref` in #Rust?
Probably only in `match` and `if let` if the borrow checker shouts at you. These are the same:
let ref x = y;
let x = &y;
`ref` is the way of saying you want to make a reference when you can only write the left side (the pattern) of the assignment.
In patterns `&` means you *expect* to see a reference, but it doesn't *make* one. These are the same:
let x = y;
let &x = &y;
Try it: https://is.gd/DIMRXy
You can disable some warnings conditionally, e.g. stop #Rustlang from complaining about unfinished code, but only in dev mode:
#![cfg_attr(debug_assertions, allow(dead_code))]
#![cfg_attr(debug_assertions, allow(unused_mut))]
#![cfg_attr(debug_assertions, allow(unused_variables))]
#![cfg_attr(debug_assertions, allow(unused_imports))]
#![cfg_attr(debug_assertions, allow(unused_parens))]
cfg_attr works with any config variable, such as feature flags, target OSes, and test builds.
Generic value in a struct doesn't live long enough?
If you want a struct that explicitly holds a reference to some generic type, this won't be enough:
struct Foo<T>(&T);
#Rustlang requires not only explicit lifetime:
struct Foo<'a, T>(&'a T);
but also specifying that any lifetimes hidden in the type `T` outlive it:
struct Foo<'a, T: 'a>(&'a T);
BTW: `struct Foo<T>(T)` can hold references too (since *any* type includes reference types, too).
#Rust .collect() cannot infer type?
let data = vec![1,2,3].iter().map(|x|x*x).collect();
^^^^ cannot infer type for `_`
The expression is ambiguous, because .collect() can create not just Vec, but HashMap, String, and others as well: https://doc.rust-lang.org/std/iter/trait.FromIterator.html
Specify the type you want. It's not necessary to type it in full, e.g. `Vec<_>` is enough.
let data: Vec<_> = iter.collect();
let data = iter.collect::<Vec<_>>();
Try it: https://is.gd/yMg2gs
Want to get multiple mutable slices from a vector?
#Rustlang will complain about:
let range_a = &mut vec[0..10];
let range_b = &mut vec[10..];
Because the first mutable borrow is for the entire `Vec`. Rust doesn't understand that the ranges are non-overlapping. The solution is to use `.split_at_mut()`:
let (range_a, range_b) = vec.split_at_mut(10);
If you want to completely split a Vec into sub-ranges, see `vec.chunks_mut()`.
Try it: https://is.gd/Q5c8vD
`if let` of multiple values?
There can be only one `let`, but it can be a tuple that expects multiple things. This is equivalent of `a && b`:
if let (Some(a), Some(b)) = (a, b) {}
For `a || b` you can use the `.or()` method of `Option` and `Result`:
if let Some(a_or_b) = a.or(b) {}
Try it: https://is.gd/SJNWQG
When Rust doesn't allow you to unwrap an `Option`, use `.as_ref()`.
Unwrapping `&Option<Foo>` causes "cannot move out of borrowed content" error, because it assumes you want to take ownership of `Foo`, and that's not allowed through a reference.
`option.as_ref()` flips it inside-out to become `Option<&Foo>`, and `unwrap()` will get a reference rather than ownership, which is fine.
Try it: https://is.gd/3lPJqk