Yusuke NOJIMA
Yusuke NOJIMA

Reputation: 255

Creating an alias for a Fn trait results in "one type is more general than the other" error

I learned an idiom to give an alias to trait. But, when I applied this idiom to my code, I encountered a puzzling error.

Here is a simplified version of the code:

// `StrToStr` is an alias for `Fn(&str) -> &str`
trait StrToStr: Fn(&str) -> &str {}
impl<T> StrToStr for T where T: Fn(&str) -> &str {}

// A function that returns `StrToStr`
fn identity() -> impl StrToStr {
    |s: &str| s
}

When I compile this code, I get the following error:

error[E0308]: mismatched types
  --> src/main.rs:77:18
   |
77 | fn identity() -> impl StrToStr {
   |                  ^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected reference `&str`
              found reference `&str`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `rust-calc` due to previous error

After trying several times with different return types, this error seems to occur when returning a reference type. However, I do not know why this error occurs and how to avoid it.

I would appreciate your advice.

Upvotes: 5

Views: 835

Answers (1)

Yusuke NOJIMA
Yusuke NOJIMA

Reputation: 255

I have found a work-around for this problem in a GitHub discussion. First, we define a no-op function to help in the type inference of the closure.

fn generalize_lifetime<F>(f: F) -> F
where
    F: Fn(&str) -> &str,
{
    f
}

Then, wrap the closure with generalize_lifetime and the compilation will pass.

// `StrToStr` is an alias for `Fn(&str) -> &str`
trait StrToStr: Fn(&str) -> &str {}
impl<F> StrToStr for F where F: Fn(&str) -> &str {}

// A function that returns `StrToStr`
fn identity() -> impl StrToStr {
    generalize_lifetime(|s| s)
}

Upvotes: 2

Related Questions