在递归函数中使用impl Trait

时间:2019-01-04 03:58:38

标签: rust

我一直在尝试impl Trait,在构建递归函数时遇到了这个错误:

error[E0308]: if and else have incompatible types
  --> src/main.rs:16:5
   |
16 | /     if logic {
17 | |         one(false)
18 | |     } else {
19 | |         two()
20 | |     }
   | |_____^ expected opaque type, found a different opaque type
   |
   = note: expected type `impl Meow` (opaque type)
              found type `impl Meow` (opaque type)

以下是要复制的代码(Rust playground link):

trait Meow {
    fn meow();
}

struct Cat(u64);

impl Meow for Cat {
    fn meow() {}
}

fn one(gate: bool) -> impl Meow {
    if gate {
        one(false)
    } else {
        two()
    }
}

fn two() -> impl Meow {
    Cat(42)
}

fn main() {
    let _ = one(true);
}

我一直无法找到有关此特定问题的文档,但奇怪的是,编译器返回一个错误,该错误粗略地说“这两个相同的事物是不同的”。

请问有什么方法可以支持impl Trait语法吗?

2 个答案:

答案 0 :(得分:5)

免责声明:此答案假定读者理解-> impl Trait需要返回单个类型;参见this question for returning different types


不透明度

Rust的核心原则之一是类型检查完全由功能,类型等的接口驱动,而实现则被忽略。

关于-> impl Trait功能,这通过将每个-> impl Trait视为不透明类型的语言来体现,仅由其来源的功能来识别。

因此,您可以调用同一函数两次:

use std::fmt::Debug;

fn cat(name: &str) -> impl Debug { format!("Meow {}", name) }

fn meow(g: bool) -> impl Debug {
    if g {
        cat("Mario")
    } else {
        cat("Luigi")
    }
}

fn main() {
    println!("{:?}", meow(true));
}

但是,即使在-> impl Trait后面隐藏了至少一个函数,即使它们返回相同的类型,也无法调用它们:

use std::fmt::Debug;

fn mario() -> impl Debug { "Meow Mario" }

fn luigi() -> &'static str { "Meow Luigi" }

fn meow(g: bool) -> impl Debug {
    if g {
        mario()
    } else {
        luigi()
    }
}

fn main() {
    println!("{:?}", meow(true));
}

收益:

  
error[E0308]: if and else have incompatible types
  --> src/main.rs:8:9
   |
8  | /         if g {
9  | |             mario()
10 | |         } else {
11 | |             luigi()
12 | |         }
   | |_________^ expected opaque type, found &str
   |
   = note: expected type `impl std::fmt::Debug`
              found type `&str`

-> impl Trait后隐藏了两个:

use std::fmt::Debug;

fn mario() -> impl Debug { "Meow Mario" }

fn luigi() -> impl Debug { "Meow Luigi" }

fn meow(g: bool) -> impl Debug {
    if g {
        mario()
    } else {
        luigi()
    }
}

fn main() {
    println!("{:?}", meow(true));
}

产生与您相同的错误消息:

  
error[E0308]: if and else have incompatible types
  --> src/main.rs:8:5
   |
8  | /     if g {
9  | |         mario()
10 | |     } else {
11 | |         luigi()
12 | |     }
   | |_____^ expected opaque type, found a different opaque type
   |
   = note: expected type `impl std::fmt::Debug` (opaque type)
              found type `impl std::fmt::Debug` (opaque type)

与递归的交互

没有。

该语言在这里没有特殊情况下的递归,因此没有意识到在所提出的问题中,只涉及一种类型。相反,它注意到fn one(...) -> impl Meowfn two(...) -> impl Meow并得出结论,它们是不同的不透明类型,因此编译时统一是不可能的。

通过讨论递归的观点或讨论模块级可见性的观点,提交RFC来调整此方面可能是合理的。这超出了此答案的范围。


解决

唯一的可能性是确保类型为 unique ,这需要命名。在名称中捕获了类型之后,就可以将其一致地应用到需要匹配的任何地方。

我会推荐您使用@Anders' answer来解决他的聪明方法。

答案 1 :(得分:4)

我认为理想的编译器会接受您的代码,但是当前的语言不允许进行递归推理,因此需要在这种情况下才能确定类型实际上是相同的。您可以通过使用类型变量抽象'wagtail.core.middleware.SiteMiddleware', 'wagtail.contrib.redirects.middleware.RedirectMiddleware', 类型来解决此丢失的功能:

impl Meow

Rust playground link