Rust tips is a user on octodon.social. You can follow them or interact with them if you have an account anywhere in the fediverse. If you don't, you can sign up here.

Rust tips @rust@octodon.social

In 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: doc.crates.io/manifest.html#th (great for monorepos).

You can also use { git = "URL" }
doc.crates.io/specifying-depen

A file path in 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: is.gd/01ZiqK

For using the `?` operator, the easiest (laziest) function return type is `Result<…, Box<Error>>`.

Almost all 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: 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 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: 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: 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`: is.gd/qo9fVj

This is the permalink for the full archive of tips: octodon.social/@rust

In `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: is.gd/8gfXQX

When to use `ref` in ?

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: is.gd/DIMRXy

You can disable some warnings conditionally, e.g. stop 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);

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).

.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: doc.rust-lang.org/std/iter/tra

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: is.gd/yMg2gs

Want to get multiple mutable slices from a vector?

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: 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: 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: is.gd/3lPJqk