camccar
camccar

Reputation: 730

How to return a Result containing a serde_json::Value?

This is what I have, but I want to avoid using unwrap on my reqwest values:

extern crate base64;
extern crate reqwest;

use serde_json;

use serde_json::json;

pub fn perform_get(id: String) -> serde_json::value::Value {
    let client = reqwest::Client::builder().build().unwrap();

    let url = String::from("SomeURL");

    let res = client.get(&url).send().unwrap().text();

    let mut v = json!(null);
    match res {
        Ok(n) => {
            v = serde_json::from_str(&n).unwrap();
        }
        Err(r) => {
            println!("Something wrong happened {:?}", r);
        }
    }

    v
}

fn main() {
    println!("Hi there! i want the function above to return a result instead of a Serde value so I can handle the error in main!");
}

Here is a link to a rust playground example

Upvotes: 1

Views: 4346

Answers (3)

Stargateur
Stargateur

Reputation: 26727

If you are in the user side I would suggest to use Box<dyn std::error::Error>, this allow to return every type that implement Error, ? will convert the concrete error type to the dynamic boxed trait, this add a little overhead when there is an error but when error are not expected or really rare this is not a big deal.

use reqwest;

use serde_json::value::Value;
use std::error::Error;

fn perform_get(_id: String) -> Result<Value, Box<dyn Error>> {
    let client = reqwest::Client::builder().build()?;

    let url = String::from("SomeURL");

    let res = client.get(&url).send()?.text()?;

    let v = serde_json::from_str(&res)?;

    Ok(v)

    // last two line could be serde_json::from_str(&res).map_err(std::convert::Into::into)
}

fn main() {
    println!("{:?}", perform_get("hello".to_string()));
}

This produce the following error:

Err(Error { kind: Url(RelativeUrlWithoutBase), url: None })

Upvotes: 3

Shepmaster
Shepmaster

Reputation: 430861

The official Rust book, The Rust Programming Language, is freely available online. It has an entire chapter on using Result, explaining introductory topics such as the Result enum and how to use it.

How to return a Result containing a serde_json::Value?

The same way you return a Result of any type; there's nothing special about Value:

use serde_json::json; // 1.0.38

pub fn ok_example() -> Result<serde_json::value::Value, i32> {
    Ok(json! { "success" })
}

pub fn err_example() -> Result<serde_json::value::Value, i32> {
    Err(42)
}

If you have a function that returns a Result, you can use the question mark operator (?) to exit early from a function on error, returning the error. This is a concise way to avoid unwrap or expect:

fn use_them() -> Result<(), i32> {
    let ok = ok_example()?;
    println!("{:?}", ok);

    let err = err_example()?;
    println!("{:?}", err); // Never executed, we always exit due to the `?`

    Ok(()) // Never executed
}

This is just a basic example.

Applied to your MCVE, it would look something like:

use reqwest; // 0.9.10
use serde_json::Value; // 1.0.38

type Error = Box<dyn std::error::Error>;

pub fn perform_get(_id: String) -> Result<Value, Error> {
    let client = reqwest::Client::builder().build()?;

    let url = String::from("SomeURL");

    let res = client.get(&url).send()?.text()?;

    let v = serde_json::from_str(&res)?;

    Ok(v)
}

Here, I'm using the trait object Box<dyn std::error::Error> to handle any kind of error (great for quick programs and examples). I then sprinkle ? on every method that could fail (i.e. returns a Result) and end the function with an explicit Ok for the final value.

Note that the panic and the never-used null value can be removed with this style.

See also:

better practice to return a Result

See also:

Upvotes: 6

camccar
camccar

Reputation: 730

The kind smart folks over at Rust Discord helped me solve this one. (user noc)

extern crate base64;
extern crate reqwest;

pub fn get_jira_ticket() -> Result<serde_json::value::Value, reqwest::Error> {
    let client = reqwest::Client::builder().build().unwrap();

    let url = String::from("SomeURL");

    let res = client.get(&url).send().and_then(|mut r| r.json());

    res
}

fn main() {
    println!("This works");
}

The key part was this in the header for the return

-> Result<serde_json::value::Value, reqwest::Error>

And this here to actually return the data.

client.get(&url).send().and_then(|mut r| r.json());

Upvotes: -1

Related Questions