作为学习生锈的项目,我正在编写一个可以解析sgf文件的程序(一种用于存储游戏的格式,以及技术上还有其他游戏)。目前该程序应该解析该类型的字符串(这只是一个例子)";B[ab]B[cd]W[ef]B[gh]"
到[Black((0,1)),Black((2,3,)),White((4,5)),Black((6,7))]
为此我使用的是parser-combinators库。
我遇到了以下错误:
main.rs:44:15: 44:39 error: can't infer the "kind" of the closure; explicitly annotate it; e.g. `|&:| {}` [E0187]
main.rs:44 pmove().map(|m| {Property::White(m)})
^~~~~~~~~~~~~~~~~~~~~~~~
main.rs:44:15: 44:39 error: mismatched types:
expected `closure[main.rs:39:15: 39:39]`,
found `closure[main.rs:44:15: 44:39]`
(expected closure,
found a different closure) [E0308]
main.rs:44 pmove().map(|m| {Property::White(m)})
^~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to 2 previous errors
Could not compile `go`.
有问题的功能如下。我是生锈的新手,所以我无法真正隔离问题,或者在没有解析器 - 组合器库的情况下重新创建它(甚至可能与该库有关系吗?)。
fn parse_go_sgf(input: &str) -> Vec<Property> {
let alphabetic = |&:| {parser::satisfy(|c| {c.is_alphabetic()})};
let prop_value = |&: ident, value_type| {
parser::spaces().with(ident).with(parser::spaces()).with(
parser::between(
parser::satisfy(|c| c == '['),
parser::satisfy(|c| c == ']'),
value_type
)
)
};
let pmove = |&:| {
alphabetic().and(alphabetic())
.map(|a| {to_coord(a.0, a.1)})
};
let pblack = prop_value(
parser::string("B"),
pmove().map(|m| {Property::Black(m)}) //This is where I am first calling the map function.
);
let pwhite = prop_value(
parser::string("W"),
pmove().map(|m| {Property::White(m)}) //This is where the compiler complains
);
let pproperty = parser::try(pblack).or(pwhite);
let mut pnode = parser::spaces()
.with(parser::string(";"))
.with(parser::many(pproperty));
match pnode.parse(input) {
Ok((value, _)) => value,
Err(err) => {
println!("{}",err);
vec!(Property::Unkown)
}
}
}
所以我猜这与所有具有不同类型的闭包有关。但在其他情况下,似乎可以使用不同的闭包调用相同的函数。例如
let greater_than_forty_two = range(0, 100)
.find(|x| *x > 42);
let greater_than_forty_three = range(0, 100)
.find(|x| *x > 43);
似乎工作正常。
所以在我的情况下发生了什么不同。
此外,正如我刚刚学习的那样,对代码的任何一般性评论也是受欢迎的。
答案 0 :(得分:4)
不幸的是,你偶然发现了Rust类型系统中的一个粗糙边缘(鉴于解析器 - 组合器的封闭性质,并非真正意外)。
以下是您的问题的简化示例:
fn main() {
fn call_closure_fun<F: Fn(usize)>(f: F) { f(12) } // 1
fn print_int(prefix: &str, i: usize) { println!("{}: {}", prefix, i) }
let call_closure = |&: closure| call_closure_fun(closure); // 2
call_closure(|&: i| print_int("first", i)); // 3.1
call_closure(|&: i| print_int("second", i)); // 3.2
}
它提供与您的代码完全相同的错误:
test.rs:8:18: 8:47 error: mismatched types:
expected `closure[test.rs:7:18: 7:46]`,
found `closure[test.rs:8:18: 8:47]`
(expected closure,
found a different closure) [E0308]
test.rs:8 call_closure(|&: i| print_int("second", i));
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
我们(在代码中的注释中引用):
Rust关闭是未装箱的。这意味着对于每个闭包,编译器生成一个新类型,它实现一个闭包特征(Fn
,FnMut
,FnOnce
)。这些类型是匿名的 - 它们没有您可以写出的名称。你所知道的是这些类型实现了某种特性。
Rust是一种强类和静态类型语言:编译器必须在编译时知道每个变量和每个参数的确切类型。因此,它必须为您编写的每个闭包的每个参数分配类型。但是(2)的closure
参数应该具有什么类型?理想情况下,它应该是一些泛型类型,就像在(1)中一样:只要它实现了特征,闭包就应该接受任何类型。但是,Rust闭包不能是通用的,因此没有语法来指定它。所以Rust编译器做了最自然的事情 - 它根据closure
的第一次使用推断call_closure
参数的类型,即来自3.1
调用 - 也就是说,它指定了匿名3.1
中的闭包类型!</ p>
但是这个匿名类型与3.2
中的闭包的匿名类型不同:它们唯一的共同点是它们都实现了Fn(usize)
。这正是错误所在。
最好的解决方案是使用函数而不是闭包,因为函数可以是通用的。不幸的是,你也无法做到这一点:你的闭包返回内部包含闭包的结构,比如
pub struct Satisfy<I, Pred> { ... }
其中Pred
后来被约束为Pred: FnMut(char) -> bool
。同样,因为闭包具有匿名类型,所以不能在类型签名中指定它们,因此您将无法写出此类泛型函数的签名。
事实上,以下工作确实有效(因为我已经为parser::satisfy()
调用参数提取了闭包):
fn prop_value<'r, I, P, L, R>(ident: I, value_type: P, l: L, r: R) -> pp::With<pp::With<pp::With<pp::Spaces<&'r str>, I>, pp::Spaces<&'r str>>, pp::Between<pp::Satisfy<&'r str, L>, pp::Satisfy<&'r str, R>, P>>
where I: Parser<Input=&'r str, Output=&'r str>,
P: Parser<Input=&'r str, Output=Property>,
L: Fn(char) -> bool,
R: Fn(char) -> bool {
parser::spaces().with(ident).with(parser::spaces()).with(
parser::between(
parser::satisfy(l),
parser::satisfy(r),
value_type
)
)
}
你会像这样使用它:
let pblack = prop_value(
parser::string("B"),
pmove().map(|&: m| Property::Black(m)),
|c| c == '[', |c| c == ']'
);
let pwhite = prop_value(
parser::string("W"),
pmove().map(|&: m| Property::White(m)),
|c| c == '[', |c| c == ']'
);
pp
与use parser::parser as pp
一起推出。
这确实有效,但实际上很难看 - 我不得不使用编译器错误输出来实际确定所需的返回类型。随着功能的微小变化,它将不得不再次调整。理想情况下,这是通过未装箱的抽象返回类型解决的 - 它们上有a postponed RFC - 但我们还没有。
答案 1 :(得分:3)
作为解析器组合器的作者,我将简单介绍另一种解决方法,而无需使用编译器生成返回类型。
由于每个解析器基本上只是一个函数和2个相关类型,因此所有函数类型都有Parser特征的实现。
impl <I, O> Parser for fn (State<I>) -> ParseResult<O, I>
where I: Stream { ... }
pub struct FnParser<I, O, F>(F);
impl <I, O, F> Parser for FnParser<I, O, F>
where I: Stream, F: FnMut(State<I>) -> ParseResult<O, I> { ... }
一旦孤儿检查允许,这些都应该被单个特征替换并移除FnParser类型。与此同时,我们可以使用FnParser类型从闭包中创建解析器。
使用这些特征,我们基本上可以隐藏Vladimir Matveev的例子中返回的大解析器类型。
fn prop_value<'r, I, P, L, R>(ident: I, value_type: P, l: L, r: R, input: State<&'r str>) -> ParseResult<Property, &'r str>
where I: Parser<Input=&'r str, Output=&'r str>,
P: Parser<Input=&'r str, Output=Property>,
L: Fn(char) -> bool,
R: Fn(char) -> bool {
parser::spaces().with(ident).with(parser::spaces()).with(
parser::between(
parser::satisfy(l),
parser::satisfy(r),
value_type
)
).parse_state(input)
}
我们现在可以使用此
构造解析器let parser = FnParser(move |input| prop_value(ident, value_type, l, r, input));
这基本上是我们目前使用生锈所能做的最好的事情。未装箱的匿名返回类型会使所有这些变得非常容易,因为不需要复杂的返回类型(也不会创建,因为可以编写库本身来利用它,完全避免使用复杂类型)。
答案 2 :(得分:2)
Rust的闭包的两个方面导致了你的问题,一个,闭包不能是通用的,两个,每个闭包都是它自己的类型。由于closure不能是通用的,prop_value
的参数value_type
必须是特定类型。因为每个闭包都是特定类型,所以传递给prop_value
中的pwhite
的闭包与pblack
中的闭包不同。编译器所做的是得出结论:value_type
必须具有pblack
中闭包的类型,当它到达pwhite
时,它会找到不同的闭包,并给出错误。
从您的代码示例来看,最简单的解决方案可能是使prop_value
成为通用fn
- 看起来它不需要是一个闭包。或者,您可以将其参数value_type
声明为闭包特征对象,例如&Fn(...) -> ...
。这是一个简化的例子,展示了这些方法:
fn higher_fn<F: Fn() -> bool>(f: &F) -> bool {
f()
}
let higher_closure = |&: f: &Fn() -> bool | { f() };
let closure1 = |&:| { true };
let closure2 = |&:| { false };
higher_fn(&closure1);
higher_fn(&closure2);
higher_closure(&closure1);
higher_closure(&closure2);
答案 3 :(得分:1)
现有的答案很好,但我想分享一个更小的问题例子:
fn thing<F: FnOnce(T), T>(f: F) {}
fn main() {
let caller = |&: f| {thing(f)};
caller(|&: _| {});
caller(|&: _| {});
}
当我们定义caller
时,其签名尚未完全修复。当我们第一次调用它时,类型推断设置输入和输出类型。在此示例中,在第一次调用之后,将需要caller
来获取具有特定类型的闭包,即第一个闭包的类型。这是因为每个闭包都有自己独特的匿名类型。当我们第二次调用caller
时,第二个闭包的(唯一的,匿名的)类型不合适!
正如@wingedsubmariner指出的那样,没有办法用泛型类型创建闭包。如果我们有像for<F: Fn()> |f: F| { ... }
这样的假设语法,那么也许我们可以解决这个问题。制作通用功能的建议很好。