Using an API to populate a Vec Struct. How do i use the data in other functions?

Im new to Rust and i cant for the life of me figure out how to actually use the Data that I have in my Struct. Im pulling the Data from an API and it changes roughly every few seconds. This is what i have so far

use serde::{Serialize, Deserialize};


#[derive(Debug, Serialize, Deserialize)]
pub struct KujiraAsset {
    pub tickers: Vec<KujiraAssetInfo>,
}

#[derive(Debug, Serialize, Deserialize)]
 pub struct KujiraAssetInfo {
    pub ask:String,
    pub base_currency: String,
    pub bid:String,
    pub pool_id: String,
    pub target_currency: String,
    pub ticker_id: String,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {


        
  
    let resp = reqwest::get("https://api.kujira.app/api/coingecko/tickers").await?;



    let resp_json = resp.json::<KujiraAsset>()
        .await?;

        
     println!("Responce Json: {:#?}", resp_json);
        Ok(())

    
     
}

it returns

Responce Json: KujiraAsset {
tickers: [
KujiraAssetInfo {
ask: "1.1130000000",
base_currency: "KUJI",
bid: "1.0930000000",
pool_id: "kujira14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sl4e867",
target_currency: "axlUSDC",
ticker_id: "KUJI_axlUSDC",
},
KujiraAssetInfo {
ask: "1.9700000000",
base_currency: "EVMOS",
bid: "1.6800000000",
pool_id: "kujira182nff4ttmvshn6yjlqj5czapfcav9434l2qzz8aahf5pxnyd33tsz30aw6",
target_currency: "axlUSDC",
ticker_id: "EVMOS_axlUSDC",
....
KujiraAssetInfo {
ask: "1.4640000000",
base_currency: "SCRT",
bid: "1.4260000000",
pool_id: "kujira1fkwjqyfdyktgu5f59jpwhvl23zh8aav7f98ml9quly62jx2sehysqa4unf",
target_currency: "axlUSDC",
ticker_id: "SCRT_axlUSDC",

is there a way for me to bind the values in each field to a variable that i can use elsewhere?

Any help or insights is always greatly appreciated

Can you specify a bit more what you are trying to do? Because right know resp_json has a single field which is a Vec<KujiraAsset>. So which fields are you trying to move into another variable?

I want to be able to reference specific fields for specific values. For instance if i want to use the "ask" value that the API pulls in for the "base_currency" SCRT. Is there a way that i can make that value into a mut variable that i can use elsewhere in the code.
ex.


let mut kuji_bid = KujiraAssetInfo "bid" in the same struct as the one containing "base_currency" KUJI

let mut scrt_bid = KujiraAssetInfo "bid" in the same struct as the one containing "base_currency" SCRT

how about something like...

let kuji_bid = resp_json.into_iter().find(|x| x.base_currency == "KUJI").bid;
1 Like

I'm getting an error that the main Struct that's used as a wrapper "KujiraAsset" is not an Iterator.

I think they meant resp_json.tickers.into_iter()...

Although saying that, you can only into_iter() once, so if you need both currencies, the find will only let you get one of them with that approach. Might want to use .iter() instead.

4 Likes

yeah you are correct, i meant that

The new error I'm seeing is "no field bid on type `std::option::Option<&KujiraAssetinfo>. Is there a different library i need to use in order to use this ?

That is because Iterator::find returns an Option that is either Some(element) or None.

The "quick and dirty" way to convert the option into the element is to use Option::unwrap, which returns the element (i.e. &KujiraAssetinfo when you call it on Option<&KujiraAssetinfo>). Using .unwrap(), however, will panic if the element was not found and find returned None.

Otherwise you might want to use a match expression with pattern matching, or something like:

let mut kuji_bid = if let Some(info) = resp_json.into_iter().find(|x| x.base_currency == "KUJI") {
    info.bid
} else {
    // return from function with an error here
}

Read about the if let construct here.

Or you could use ok_or to convert the Option into a Result and then use the ? operator like that:

let mut kuji_bid = resp_json
    .into_iter()
    .find(|x| x.base_currency == "KUJI")
    .ok_or("Some error")?
    .bid
    .clone();

I created a simplified Playground for demonstration (click on "Playground").

If creating the error value is computationally expensive (and not just using a string like "Some error" to be converted into an error), you also can consider to use ok_or_else instead.

2 Likes

Thanks for your response. in example 1 "return from function with an error"? What does that mean?

I meant a return statement, like return Err("Not found".into()); for example.

struct Info {
    currency: String,
    bid: String,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let infos = vec![
        Info { currency: "A".to_string(), bid: "1".to_string() },
        Info { currency: "B".to_string(), bid: "2".to_string() },
    ];
    let info_option = infos.iter().find(|info| info.currency == "A");
    let bid_a = if let Some(info) = info_option {
        info.bid.clone()
    } else {
        "some default".to_string()
        // or alternatively:
        // return Err("Not found".into());
    };
    println!("{bid_a}");
    Ok(())
}

(Playground)

When you write if let Some(x) = y { … } else { … }, then both blocks (the ones with {} braces) must return a value of the same type. The only exception is if you end the function with a return statement (or diverge otherwise by causing a panic). The else-branch may thys either return a value of the same type, e.g. "some default".to_string(), or you may also decide to abort the function by prematurely returning with an error (or directly cause a panic).

Try to uncomment return Err("Not found".into()); in the above example. You can then remove the line "some default".to_string() because the compiler will know that the else branch will end the function call (and thus won't force you to provide a String to be stored in bid_a).

Alternatively, you could also write panic!("Don't know what to do") instead of the return line. The point is, the else branch must provide a value or diverge, that is not finishing execution but abort execution through a premature return or panic.

2 Likes

Ok I think I understand now. Thank you for taking the time to explain it so thoroughly Funnily enough now #[tokio::main] is now showing an error along with the final curly bracket

use serde::{Serialize, Deserialize};



#[derive(Debug, Serialize, Deserialize)]
pub struct KujiraAsset {
    pub tickers: Vec<KujiraAssetInfo>,
}

#[derive(Debug, Serialize, Deserialize)]
 pub struct KujiraAssetInfo {
    pub ask:String,
    pub base_currency: String,
    pub bid:String,
    pub pool_id: String,
    pub target_currency: String,
    pub ticker_id: String,
}
// error here saying Expected item after attribute
#[tokio::main]/async fn main() -> Result<(), Box<dyn std::error::Error>> {


        
  
    let resp = reqwest::get("https://api.kujira.app/api/coingecko/tickers").await?;



    let resp_json = resp.json::<KujiraAsset>()
        .await?;

        let mut kuji_ask = if let Some(info) = resp_json.tickers.into_iter().find(|x| x.base_currency =="KUJI") {
           info.ask 
        } else {
            "some default".to_string()
        
        };

    println!("ask is {}",kuji_ask);
    
     
}//error her saying expected Result<()dyn Error,Global>> found()

Can you show more or the whole error message? Maybe you forgot a closing curly brace } somewhere ?

The above was the entire code. I commented out where the issues where. any ideas?

You forgot the last line of @jbe's post the Ok(()). The main function must return a Result, so you just have to return the Ok variant.

image
above are the errors im getting and I just added the ok(()) variant and i still have the same errors minus the last one that ln [Ln 42, Col1]

use serde::{Serialize, Deserialize};



#[derive(Debug, Serialize, Deserialize)]
pub struct KujiraAsset {
    pub tickers: Vec<KujiraAssetInfo>,
}

#[derive(Debug, Serialize, Deserialize)]
 pub struct KujiraAssetInfo {
    pub ask:String,
    pub base_currency: String,
    pub bid:String,
    pub pool_id: String,
    pub target_currency: String,
    pub ticker_id: String,
}

#[tokio::main]/async fn main() -> Result<(), Box<dyn std::error::Error>> {


        
  
    let resp = reqwest::get("https://api.kujira.app/api/coingecko/tickers").await?;



    let resp_json = resp.json::<KujiraAsset>()
        .await?;

        let mut kuji_ask = if let Some(info) = resp_json.tickers.into_iter().find(|x| x.base_currency =="KUJI") {
           info.ask 
        } else {
            "some default".to_string()
        
        };
        

    println!("ask is {}",kuji_ask);
    
    Ok(())
}

Sorry, I didn't see the scrollbar, and I thought you had omitted some code.

It's simply a syntax error now:

-#[tokio::main]/async fn main() -> Result<(), Box<dyn std::error::Error>> {
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn std::error::Error>> {

Maybe you accidentally pressed a key and removed the line break but didn't notice it.

1 Like

That must be it. thank you for all your help!!! its finally working

Here I have some ways to do it for you. I hope this is of use.


use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
pub struct KujiraAsset {
    pub tickers: Vec<KujiraAssetInfo>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct KujiraAssetInfo {
    pub ask: String,
    pub base_currency: String,
    pub bid: String,
    pub pool_id: String,
    pub target_currency: String,
    pub ticker_id: String,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let resp = reqwest::get("https://api.kujira.app/api/coingecko/tickers").await?;

    let mut resp_json = resp.json::<KujiraAsset>().await?;

    // Removing one element
    let kujira_one = resp_json.tickers.remove(0);
    println!("kujira_one: {:#?}", kujira_one);

    // Get a single field.
    let ask_one = kujira_one.ask;
    println!("ask_one: {:#?}", ask_one);

    // Remove second element
    let kujira_two = resp_json.tickers.remove(0);

    // Use pattern matching to unpack multiple values from struct.
    let KujiraAssetInfo {
        ask: ask_two,
        base_currency: base_currency_two,
        ..
    } = kujira_two;
    println!("ask_two: {:#?}", ask_two);
    println!("base_currency_two: {:#?}", base_currency_two);

    Ok(())
}
3 Likes

So this is the almost finished version. for some reason I'm getting more syntax errors with the updated version

use serde::{Serialize, Deserialize};

#[derive(Debug, Serialize, Deserialize)]
pub struct OsmosisAssetInfo {
    pub price: f32,
    pub denom: String,
    pub symbol: String,

}


#[derive(Debug, Serialize, Deserialize)]
pub struct KujiraAsset {
    pub tickers: Vec<KujiraAssetInfo>,
}

#[derive(Debug, Serialize, Deserialize)]
 pub struct KujiraAssetInfo {
    pub ask:String,
    pub base_currency: String,
    pub bid:String,
    pub pool_id: String,
    pub target_currency: String,
    pub ticker_id: String,
}

#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> {


        
  
    let resp_kuji = reqwest::get("https://api.kujira.app/api/coingecko/tickers").await?;


    let resp_osmosis = reqwest::get("https://api-osmosis.imperator.co/tokens/v2/all").await?;



    let resp_kuji_json = resp_kuji.json::<KujiraAsset>()
        .await?;

        let kuji_ask = if let Some(info) = resp_kuji_json.tickers.into_iter().find(|x| x.base_currency =="KUJI") {
           info.ask 
        } else {
            "some default".to_string()
        
        };
        //the error is underneath :: and OsmosisAsstInfo 
    let resp_osmosis_json = resp_osmosis.json()::<OsmosisAssetInfo>().await?;

        let Osmosis_price = if let Some(info) = resp_osmosis_json.price.into_iter().find(|x| x.symbol== "KUJI") {
            
        }else {
            "some default".to_string()

        };

    println!("ask on Kujira is {} and the price on Osmosis is {}", kuji_ask,Osmosis_price);
    
    Ok(()) 
}

Again I'm not sure what I've done wrong. these are the errors I'm getting
image