Skip to content

Instantly share code, notes, and snippets.

@cmyr
Last active June 23, 2022 21:58
Show Gist options
  • Save cmyr/f04c72a4bdc422c4c110bd9d625a0776 to your computer and use it in GitHub Desktop.
Save cmyr/f04c72a4bdc422c4c110bd9d625a0776 to your computer and use it in GitHub Desktop.
Rust quick tips collection

Rust Tips & Suggestions

Struct forms:

There are three struct forms: unit structs, tuple structs, and named field structs:

struct UnitStruct;
struct TupleStruct(String, usize);
struct NamedStruct { string: String, is_cuss_word: bool }

unit structs are uncommon; they can't hold any data, and are only used in certain special circumstances; you can ignore them for now.

tuple structs provide access to their contents by indexing: in I only like to use them when they at most two members, and in 90% of cases where I have two fields, I use names:

let my_tup = TupleStruct(String::from("hello"), 42);
assert_eq!(my_tup.0, String::from("hello"));
assert_eq!(my_tup.1, 42));

named structs are great, and you should use them most of the time.

Into and From

The From trait looks like this:

pub trait From<T> {
    fn from(_: T) -> Self;
}

This trait can be implemented to perform simple conversions between types.

struct Secret(String);

impl From<String> for Secret {
    fn from(inp: String) -> Secret {
        Secret(inp)
    }
}

let secret_string = String::from("kryptonite seven craton optimist");
let secret = Secret::from(secret_string); // well that looks familiar...

From implies Into

One very cool thing about From is its relationship with Into. Basically: If you impl From<T> for U, you get impl Into<U> for T for free. This means we can now do this:

let secret_string = String::from("kryptonite seven craton optimist");
let secret: Secret = secret_string.into();

More helpfully, rust's type inference means you can do things like this:

fn make_secret(input: String) -> Secret {
    s.into()
}

Because the return value of the function is a Secret, Rust can infer what into we're looking for.

All types implement From<Self>, for free. This is useful for writing generic functions can take either the expected type or any type that can be into()'d it. For instance,

fn process_secret<S: Into<Secret>>(input: s) {
    let secret = input.into();
    // do some secret stuff
}

Use with Strings

From and Into are particularly useful when working with strings. Suppose we want to be able to construct our Secret type with either a String or a &str:

impl<T> From<T> for Secret where T: Into<String> {
    fn from(s: T) -> Secret {
        Secret(s.into())
    }
}

let secret_string: Secret = String::from("kryptonite seven craton optimist").into();
let secret_str: Secret = "carnival uptown gamine idiolect".into();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment