【发布时间】:2021-06-10 11:29:46
【问题描述】:
一些背景(可以跳过):
我是 Rust 的新手,我来自 Haskell 背景(以防万一让您了解我可能有的任何误解)。
我正在尝试编写一个程序,给定来自数据库的大量输入,该程序可以创建可自定义的报告。为此,我想创建一个Field 数据类型,它可以以某种 DSL 样式组合。在 Haskell 中,我的直觉是让 Field 成为 Functor 和 Applicative 的实例,这样就可以编写这样的东西:
type Env = [String]
type Row = [String]
data Field a = Field
{ fieldParse :: Env -> Row -> a }
instance Functor Field where
fmap f a = Field $
\env row -> f $ fieldParse a env row
instance Applicative Field where
pure = Field . const . const
fa <*> fb = Field $
\env row -> (fieldParse fa) env row
$ (fieldParse fb) env row
oneField :: Field Int
oneField = pure 1
twoField :: Field Int
twoField = fmap (*2) oneField
tripleField :: Field (Int -> Int)
tripleField = pure (*3)
threeField :: Field Int
threeField = tripleField <*> oneField
实际问题:
我知道在 Rust 中实现 Functor 和 Applicative 特征是很尴尬的,所以我只是为 Field 实现了适当的函数,而不是实际定义特征(这一切都编译得很好)。这是 Rust 中 Field 的一个非常简化的实现,没有任何 Functor 或 Applicative 的东西。
use std::result;
use postgres::Row;
use postgres::types::FromSql;
type Env = Vec<String>;
type FieldFunction<A> = Box<dyn Fn(&Env, &Row) -> Result<A, String>>;
struct Field<A> {
field_parse: FieldFunction<A>
}
我可以轻松地创建一个函数,该函数只需从输入字段中获取值并使用它创建一个报告Field:
fn field_good(input: u32) -> Field<String> {
let f = Box::new(move |_: &Env, row: &Row| {
Ok(row.get(input as usize))
});
Field { field_parse: f }
}
但是当我尝试使这个多态而不是使用 String 时,我得到了一些非常奇怪的生命周期错误,我只是不明白:
fn field_bad<'a, A: FromSql<'a>>(input: u32) -> Field<A> {
let f = Box::new(move |_: &Env, row: &Row| {
Ok(row.get(input as usize))
});
Field { field_parse: f }
}
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/test.rs:36:16
|
36 | Ok(row.get(input as usize))
| ^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 35:22...
--> src/test.rs:35:22
|
35 | let f = Box::new(move |_: &Env, row: &Row| {
| ______________________^
36 | | Ok(row.get(input as usize))
37 | | });
| |_____^
note: ...so that reference does not outlive borrowed content
--> src/test.rs:36:12
|
36 | Ok(row.get(input as usize))
| ^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the function body at 34:14...
--> src/test.rs:34:14
|
34 | fn field_bad<'a, A: FromSql<'a>>(input: FieldId) -> Field<A> {
| ^^
note: ...so that the types are compatible
--> src/test.rs:36:16
|
36 | Ok(row.get(input as usize))
| ^^^
= note: expected `FromSql<'_>`
found `FromSql<'a>`
任何帮助解释这个错误实际上是什么或如何修复它都将不胜感激。我包含了 Haskell 的东西,这样我的设计意图就很清楚了,这样如果问题是我使用的编程风格在 Rust 中并不真正适用,那么可以向我指出。
编辑:
忘记包含指向postgres::Row::get 的文档的链接,以防万一。他们可以找到here。
【问题讨论】:
标签: rust lifetime borrow-checker