Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does it mean to instantiate a Rust generic with an underscore?

Tags:

generics

rust

While working with serde_json for reading json documents, I wrote the following line of code to obtain the result of unwrapping the return value of serde_json::from_str:

fn get_json_content(content_s: &str) -> Option<Value> {
    let ms: String = serde_json::from_str(content_s).unwrap; // <--

    match serde_json::from_str(content_s) {
        Ok(some_value) => Some(some_value),
        Err(_) => None
    }
}

As you can see, I forgot the () on the end of the call to unwrap, which resulted in the following error:

error: attempted to take value of method unwrap on type core::result::Result<_, serde_json::error::Error>

let ms: String = serde_json::from_str(content_s).unwrap;

But when I looked at this a bit further, the thing that struck me as odd was:

core::result::Result<_, serde_json::error::Error>

I understand what underscore means in a match context, but to instantiate a generic? So what does this mean? I couldn't find any answers in the Rust book, or reference, or a web search.

like image 621
Plastikfan Avatar asked May 13 '16 17:05

Plastikfan


People also ask

What does generic mean in Rust?

In Rust, generics refer to the parameterization of data types and traits. Generics allows to write more concise and clean code by reducing code duplication and providing type-safety. The concept of Generics can be applied to methods, functions, structures, enumerations, collections and traits.

Does Rust support generic types?

Rust can make use of generics in several places: Function Definitions. Struct Definitions. Enum Definitions.

What is Vec <_> in Rust?

It means "Rust compiler, infer what type goes into the Vec ". And it is indeed analogous to the unused variable in Python (and in Rust itself), in that it represents a placeholder for a type, like it can represent a placeholder for a variable name.


1 Answers

It's a placeholder. In this context, it means that there isn't enough information for the compiler to infer a type.

You can use this in your code to make the compiler infer the type for you. For example:

pub fn main() {
    let letters: Vec<_> = vec!["a", "b", "c"]; // Vec<&str>
}

This is particularly handy because in many cases you can avoid using the "turbofish operator":

fn main() {
    let bar = [1, 2, 3];
    let foos = bar.iter()
                  .map(|x| format!("{}", x))
                  .collect::<Vec<String>>(); // <-- the turbofish
}

vs

fn main() {
    let bar = [1, 2, 3];
    let foos: Vec<_> = bar // <-- specify a type and use '_' to make the compiler
                           //     figure the element type out
            .iter()
            .map(|x| format!("{}", x))
            .collect(); // <-- no more turbofish
}
like image 176
Wesley Wiser Avatar answered Sep 29 '22 08:09

Wesley Wiser