【问题标题】:Type annotations required for format! when parsing postgres results键入格式所需的注释!解析 postgres 结果时
【发布时间】:2018-06-19 20:47:04
【问题描述】:

我有一些代码应该从数据库中获取图像文件名并将它们添加到向量中。

extern crate postgres;

use postgres::{Connection, TlsMode};

fn main() {
    let conn = Connection::connect(
        "postgres://postgres:password@localhost:5432/test",
        TlsMode::None,
    ).unwrap();
    let mut filenames = Vec::new();

    if let Ok(filename_results) = conn.query("SELECT filename FROM images", &[]) {
        for row in &filename_results {
            filenames.push(format!("{}.jpg", row.get(0)));
        }
    }

    println!("{:?}", filenames);
}

这失败了:

error[E0283]: type annotations required: cannot resolve `_: postgres::types::FromSql`
  --> src/main.rs:14:54
   |
14 |                 filenames.push(format!("{}.jpg", row.get(0)));
   |                                                      ^^^

我不明白为什么 Rust 无法在这种情况下找出类型,尽管我已经找到了让它工作的方法。我想知道告诉format!() 它应该期待什么类型的最简单/惯用的方法是什么,以及为什么 row.get(0) 不需要类型注释,除非我在它周围加上一个格式!()。这是我对解决方案的最佳尝试:

for row in &filename_results {
    let filename: String = row.get(0);
    filenames.push(format!("{}.jpg", filename));
}

【问题讨论】:

  • 我相信How do I imply the type of the value when there are no type parameters or ascriptions?的答案已经回答了您的问题。如果您不同意,请edit您的问题解释差异。否则,我们可以将此问题标记为已回答。
  • 已编辑以添加周围代码并删除结构。我真的不明白你链接的问题与我的问题有什么关系 - 至少,我的技能水平不允许我将那个答案翻译成我的答案。我的问题涉及 psql 和格式之间的交互!我无法判断潜在问题是否与您在 impl{} 块内无法传递 &self 以外的参数时遇到的问题相同,但是有足够的差异,我无法找到那个问题。我可以说它们都是关于类型的,但仅此而已。
  • 我从你的其他评论中得到的信息是row.get::<String>(0),它失败了,因为 0 不是字符串。 row.get::<_, String>(0) 可以工作,这正是我想要的。否则,你有什么理由认为单线风格更好吗?是为了避免使“图像”可变吗?
  • 我在您的回答中没有看到的关于 turbofish 的一个奇怪的事情是,前 n 个参数是函数的参数,而最后一个参数指定了推断的返回类型。就我而言,我认为从那个答案中并不能清楚地看出我必须使用 <_ string> 来匹配 0 和 row.get() 的类型。
  • 希望你不要介意。我已经写了你在这里解决的问题作为答案。我同意 Timofey 的观点,除非您已经了解发生了什么,否则链接问题的答案是否也能回答这个问题并不明显,因此我试图解释其中的联系。

标签: rust


【解决方案1】:

让我们看看你正在调用的函数的签名:

fn get<I, T>(&self, idx: I) -> T 
where
    I: RowIndex + Debug,
    T: FromSql,

也就是说,这个函数实际上有两个类型参数,IT。它使用I 作为索引类型。您传递的参数具有这种类型。 T 是返回类型。约束(where 子句)在这里并不重要,但它们指定参数类型 I 必须是 postgres 可以用作行索引的东西,返回类型 T 必须是 postgres 的东西可以从 SQL 结果创建。

通常,Rust 可以推断函数的类型参数。参数类型通常更容易推断,因为那里有所需类型的值。甚至 C++ 也可以推断参数类型!返回类型更难推断,因为它们取决于调用函数的上下文,但 Rust 通常也可以推断出这些。

让我们看看你的函数调用和它使用的上下文:

format!("{}.jpg", row.get(0))

很明显,参数是一个整数,因为它是一个字面量,它就在那里。有一些规则可以计算出它可能是什么整数类型,但在这种情况下,它必须是 usize 因为这是唯一实现 RowIndex 特征的类型。

但是您期望的返回类型是什么? format! 几乎可以接受任何类型,因此编译器无法知道 get 需要返回什么。它只知道T 必须具有FromSql 特征。这就是错误消息告诉您的内容:

error[E0283]: type annotations required: cannot resolve `_: postgres::types::FromSql`

幸运的是,Rust 具有将函数参数显式传递给函数的语法,因此您不必依赖它的类型推断。 Shepmaster 在this answer 中为类似的问题写了一个很好的解释。直接跳到答案,你可以写row.get::&lt;_, String&gt;(0)只指定第二个类型参数,让推理在第一个类型参数上工作。

您特别要求一种更惯用的方式来指定类型,我认为您已经拥有的方式更惯用。使用显式类型参数,读者仍然需要了解get 的签名才能知道String 将是返回类型。第二个类型参数并不总是返回类型,很容易混淆并以错误的顺序指定它们。通过对结果进行命名和类型注释,您可以清楚地了解类型注释所指的值。

let filename: String = row.get(0);
filenames.push(format!("{}.jpg", filename));

如果您确实想以 Shepmaster 建议的更实用的风格编写代码,您仍然可以使用这种风格:

let filenames = filename_results.map(|row| { let f: String = row.get(0); format!("{}.jpg", f) }).collect();

如果适合您的口味,请打破“单线”。

【讨论】:

    猜你喜欢
    • 2021-08-19
    • 2018-07-07
    • 1970-01-01
    • 2011-09-29
    • 1970-01-01
    • 2011-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多