Search code examples
rustazure-cosmosdbactix-web

Rust return a result from a function


I'm quite new at using Rust. I'm trying to play around with Rust, Actix, and Azure Cosmosdb. At the moment I'm simply trying to connect to the cosmosdb and returning a result. I'm at the point where I'm happy with any result at all. At the moment I have some code in a function that should just return a string with the cosmos collection name. I can not wrap my head around how to return the string as a result. I need to return the result as it is an async function. If someone can please point me in the right way I would be very grateful. cosmos code is coming from azure_data_cosmos::prelude::*; Code below:

    async fn datastuff() -> Result<String, String> {

    let primary_key = std::env::var("COSMOSDB_READKEY").expect("Set env variable COSMOS_PRIMARY_KEY first!");
    let account = std::env::var("COSMOSDB_ACCOUNT").expect("Set env variable COSMOS_ACCOUNT first!");

    let database_name = "azdeployer";
    let collection_name = "resourcetemplates";

    // First, create an authorization token. There are two types of tokens: primary and resource constrained.
    // Please check the Azure documentation or the examples folder on how to create and use token-based permissions.
    let authorization_token = AuthorizationToken::primary_key(&primary_key)?;

    // Next we will create a Cosmos client.
    let client = CosmosClient::new(account, authorization_token);

    // We know the database so we can obtain a database client.
    let database = client.database_client(database_name);
    // We know the collection so we can obtain a collection client.
    let collection = database.collection_client(collection_name);
    //let cosmos_collection_client: CollectionClient = cosmos::cosmosclient::create_collection_client().expect("prutser");
    //let document_client: DocumentClient = cosmos_collection_client.document_client("resourceGroups@2022-09-01", "resourcegroup");
    let documents1 = collection.list_documents();
    let documents = collection.collection_name();
    let result: String = documents.to_string();
    Ok(result)

The error I'm getting is:

error[E0277]: `?` couldn't convert the error
to `std::string::String`   --> src\main.rs:17:76    | 17 |     let
authorization_token = AuthorizationToken::primary_key(&primary_key)?; 
|                                                                     
^ the trait `std::convert::From<azure_core::error::Error>` is not
implemented for `std::string::String`    |    = note: the question
mark operation (`?`) implicitly performs a conversion on the error
value using the `From` trait    = help: the following other types
implement trait `std::convert::From<T>`:
             <std::string::String as std::convert::From<char>>
             <std::string::String as std::convert::From<bytestring::ByteString>>
             <std::string::String as std::convert::From<Box<str>>>
             <std::string::String as std::convert::From<uuid::Uuid>>
             <std::string::String as std::convert::From<Cow<'a, str>>>
             <std::string::String as std::convert::From<url::Url>>
             <std::string::String as std::convert::From<&str>>
             <std::string::String as std::convert::From<&mut str>>
             <std::string::String as std::convert::From<&std::string::String>>    = note: required for
`Result<std::string::String, std::string::String>` to implement
`FromResidual<Result<Infallible, azure_core::error::Error>>`

Some errors have detailed explanations: E0277, E0425. For more
information about an error, try `rustc --explain E0277`.

Solution

  • The error is caused by the return type you declared on the function: Result<String, String>. This type indicates:

    • a successful result is a String, and
    • an error is also a String.

    The first makes sense, since the type of the result variable is String, so Ok(result) fits into the declared return type. However, the compiler is complaining about the line:

    let authorization_token = AuthorizationToken::primary_key(&primary_key)?;
    

    The ? operator is syntax sugar for checking if the operation failed, and if so, immediately returning Err from the current function with that error. It will also attempt to convert the error for convenience, based on an From implementation.

    This last part does not work out, as noted by the compiler:

    the trait `std::convert::From<azure_core::error::Error>` is not
    implemented for `std::string::String`
    

    That is, the primary_key method returns a azure_core::Result<AuthorizationToken>, which is a type alias for Result<AuthorizationToken, azure_core::error::Error>, but azure_core::error::Error cannot be converted to String.

    Possible solutions:

    • Change the return type to azure_core::Result<String>. This way the ? operator will work, since the error type already matches. However, your interface will change, so the callsite will also need to adapt.
    • unwrap the result instead of ?. This is generally bad, since you are introducing a potential source of panics (crashes) into the program, but it is the quickest change if you are trying to simply prototype something.
    • Create your own error type, which can be created from Azure's. This is what tends to happen in larger projects where multiple different frameworks interact: the application needs to be able to propagate different kinds of errors through the result, so there is an enum with variants for Azure errors, for filesystem errors, etc etc.