Confusion with Rust generics and Type checking

I have a function with the following signature

pub fn get<'de, T>(key: &'de str) -> Result<T>
    where
        T: serde::Deserialize<'de>,

The returned type can be anything that implements serde::Deserialize. I can do something like this.

let graphql_url: String = AppConfig::get("graphql.url").unwrap();

This code works correctly. It gets the value of "graphql.url" as a "String". The type coercion seems to work. I can use the String as a &str with another function. This function is graphiql_source (juniper::http::graphiql::graphiql_source - Rust) which takes a &str.

graphiql_source(&graphql_url); // -> this code compiles and runs.

So where is the problem? The problem is when I try to do this.

graphiql_source(AppConfig::get("graphql.url").unwrap());

My understanding is that Rust will need to coerce "AppConfig::get("graphql.url").unwrap()" into a &str. This code compiles but as soon as it gets executed, I get the following error

inner: invalid type: string "http://127.0.0.1:4556", expected a borrowed string

So the type of "AppConfig::get("graphql.url").unwrap()" is a String. But the function accepts a &str. How come this code is compiling in the first place?

serde supports deserializing straight into a &str, there's no Rust coercion here; signature of graphiql_source is causing the compiler to select T: &str, and supplies that type to your get(), which in turn supplies it to serde. It looks like serde is complaining (i.e. this is a runtime serde error) that it's unable to deserialize into an &str. I believe if the string contains some characters that need to be escaped, it will not be able to deserialize into an &str.

1 Like

It won't be able to deserialize into an &str (independent of escaping), if the deserializer isn't working off of a user-provided buffer/slice (e.g. when it's deserializing from an io::Read, for example, a file or a socket). In these cases, the deserializer has to create its own internal buffer within the scope of the Deserialize::deserialize impl (or an inner helper function thereof), to which a reference of course can't be returned.

(Incidentally, this is one of the reasons why deserialization is driven by a visitor; otherwise, e.g. even simple enums would have to be deserialized from a full-blown owned String, which would be hugely wasteful.)

3 Likes