0

I've been toying around with Rust and have come across the following code:

fn request(&url) -> Result<(), Box<dyn std::error::Error>> {
  let mut res = reqwest::get(&url)?;
  let mut body = String::new();
  res.read_to_string(&mut body)?;
  println!("Status: {}", res.status());
  println!("Headers:\n{:#?}", res.headers());
  println!("Body:\n{}", body);
  Ok(())
}

It is my understanding that:

fn request(&url) -> Result<(), Box<dyn std::error::Error>> {

Defines a function that has a single (borrowed) parameter and uses Result to handle errors.

  let mut res = reqwest::get(&url)?;

Defines a mutable variable to store the response object from the reqwest crate's get method.

  let mut body = String::new();

Defines a mutable variable to store the responseText string.

  res.read_to_string(&mut body)?;

This method stores the responseText in the body variable.

  println!("Status: {}", res.status());
  println!("Headers:\n{:#?}", res.headers());
  println!("Body:\n{}", body);

Prints three formatted strings (with trailing new lines) containing the response status, headers and body.

  Ok(())

Handles errors via Result..?


Questions:

  1. What do the empty parenthesis in Result<() and OK(()) mean/do?
  2. What is Box<dyn std::error::Error>?
Malekai
  • 4,765
  • 5
  • 25
  • 60

1 Answers1

2

You're absolutely correct in your understanding.

  1. A Result is an Enum which can either be "Ok" or "Err" - if Ok, then there can be some value of okayness (a result, response, data, output, whatever); similarly, if Err, then there's some concrete error you may want to communicate. With that let's break down the result.

    The should be read like this: Result<TypeOfValueIfOkay, TypeOfErrorWhenNotOkay>. These two sub-types can be anything, but they have to be something - can't just ignore it.

  2. So if TypeOfValueIfOkay has to be something, but if you don't want to return something, you can return an empty Tuple. That's the () in Result. It's just efficiently saying "I return nothing at all when everything goes well".

  3. So then the second part TypeOfErrorWhenNotOkay can also just be any type - a string, an int, whatever. It helps for the type to implement the std::error::Error trait helping callers standardize a bit.

    Returning "some dynamic object but that implements trait std::error::Error" requires Rust to know the exact size of this value if it is to return it on the caller's stack (the caller's stack needs to be sized to accept it.)

    This is where the Box type comes in - it pushes the actual value onto the heap and holds a pointer to it (which can be of predictable fixed size no matter the actual value on the heap.) The <dyn std::error::Error> is an assurance that whatever the boxed value is, it implements the Error trait.

  4. So now the final Ok(()) makes sense. If you read Ok(value): it says the Result enum is variant Ok with the value of "empty tuple" (), i.e. nothing.

user400483
  • 116
  • 1
  • 2
  • 1
    Thank you for taking the time to answer my question, I appreciate it. Also, Welcome to StackOverflow! – Malekai May 06 '20 at 13:21