Cannot convert &'static str to String in generic method

Hello,

I'm using rust 1.53, and I can't figure why this code does not compile.

fn test<T>(arg_t: T)
where
    String: std::convert::From<T>,
{
    let _a = String::from(arg_t);
    let _c = String::from("TEST");
}

with the following error

error[E0308]: mismatched types
  --> src/main.rs:21:27
   |
16 | fn test<T>(arg_t: T)
   |         - this type parameter
...
21 |     let _c = String::from("TEST");
   |                           ^^^^^^ expected type parameter `T`, found `&str`
   |
   = note: expected type parameter `T`
                   found reference `&'static str`

More confusing to me is that this compiles

fn test<T, U>(arg_t: T, arg_u: U)
where
    String: std::convert::From<T>,
    String: std::convert::From<U>,
{
    let _a = String::from(arg_t);
    let _b = String::from(arg_u);
    let _c = String::from("TEST");
}

Am I missing something or is this a known bug ?

Thanks in advance.

Seems similar to e.g. this issue

https://github.com/rust-lang/rust/issues/84218

In my opinion, this behavior of Rust’s type inference is confusing, so with some luck, maybe eventually it could change. In the case of your specific example, you might want to choose an Into-based bound anyways, since that’s generally recommended anyways (Into docs containing quote below)

Prefer using Into over From when specifying trait bounds on a generic function to ensure that types that only implement Into can be used as well.

So then the code becomes

fn test<T>(arg_t: T)
where
    T: Into<String>
{
    let _a: String = arg_t.into();
    let _c = String::from("TEST");
}

Since this is only a problem with type inference, the original code can also be fixed by being more explicit about the trait that’s used:

fn test<T>(arg_t: T)
where
    String: std::convert::From<T>,
{
    let _a = String::from(arg_t);
    let _c = <String as From<&str>>::from("TEST");
}

or you can use a different method of &str to String conversion, e.g.

fn test<T>(arg_t: T)
where
    String: std::convert::From<T>,
{
    let _a = String::from(arg_t);
    let _c = "TEST".to_owned();
}

or

fn test<T>(arg_t: T)
where
    String: std::convert::From<T>,
{
    let _a = String::from(arg_t);
    let _c: String = "TEST".into();
}

or

fn test<T>(arg_t: T)
where
    String: std::convert::From<T>,
{
    let _a = String::from(arg_t);
    let _c = "TEST".to_string();
}
1 Like

Looks like it, indeed. I encountered this behavior, and although I was able to work around it, I wanted to understand it. Anyway, thanks for the answer.

I'll try switching to the Into trait, thanks for the tip.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.