为什么一个lazy-static的值声称自己没有实现一个明显已经实现的trait?

12
使用以下代码(试图使用reqwest crate发出HTTP请求),编译器说我的值SID_URI没有实现traitPolyfillTryInto。这是怎么回事?reqwest::Url 显然实现了私有trait reqwest::into_url::PolyfillTryInto
#[macro_use]
extern crate lazy_static;
extern crate reqwest;

static R_EMAIL: &str = "example@example.com";
static R_PASS: &str = "password";
static API_PUBKEY: &str = "99754106633f94d350db34d548d6091a";
static API_URI: &str = "https://example.com";
static AUTH_PATH: &str = "/api/v1";

lazy_static! {
    static ref SID_URI: reqwest::Url = reqwest::Url::parse(&(API_URI.to_owned() + AUTH_PATH)).unwrap();
}

fn get_sid() -> Result<reqwest::Response, reqwest::Error> {
    let client = reqwest::Client::new();
    let params = [("ID", R_EMAIL), ("PW", R_PASS), ("KY", API_PUBKEY)];
    let q = client.post(SID_URI).form(&params).send()?;
    Ok(q)
}

fn main() {
    assert!(get_sid().is_ok());
}
error[E0277]: the trait bound `SID_URI: reqwest::into_url::PolyfillTryInto` is not satisfied
  --> src/main.rs:19:20
   |
19 |     let q = client.post(SID_URI).form(&params).send()?;
   |                    ^^^^ the trait `reqwest::into_url::PolyfillTryInto` is not implemented for `SID_URI`
   |
   = note: required because of the requirements on the impl of `reqwest::IntoUrl` for `SID_URI`
1个回答

28
编译器没有欺骗你,只是你忽略了错误信息中一个相关的细节。以下是一个自包含的例子:
#[macro_use]
extern crate lazy_static;

struct Example;
trait ExampleTrait {}
impl ExampleTrait for Example {}

lazy_static! {
    static ref EXAMPLE: Example = Example;
}

fn must_have_trait<T>(_: T)
where
    T: ExampleTrait,
{
}

fn main() {
    must_have_trait(EXAMPLE);
    must_have_trait(42i32);
}

error[E0277]: the trait bound `EXAMPLE: ExampleTrait` is not satisfied
  --> src/main.rs:19:5
   |
19 |     must_have_trait(EXAMPLE);
   |     ^^^^^^^^^^^^^^^ the trait `ExampleTrait` is not implemented for `EXAMPLE`
   |
   = note: required by `must_have_trait`

error[E0277]: the trait bound `i32: ExampleTrait` is not satisfied
  --> src/main.rs:20:9
   |
20 |         must_have_trait(42i32);
   |         ^^^^^^^^^^^^^^^ the trait `ExampleTrait` is not implemented for `i32`
   |
   = note: required by `must_have_trait`

比较这两个错误信息:

the trait bound `EXAMPLE: ExampleTrait` is not satisfied
the trait bound `i32: ExampleTrait` is not satisfied

第二个错误信息并没有说 42 没有实现 ExampleTrait,它说的是 i32 没有实现。这个错误信息显示了失败的类型,而不是值的名称!这意味着在相同的上下文中,EXAMPLE 是指一个类型。
Lazy-static 通过创建一次性类型来工作,这些类型包装您的值并提供线程安全的单初始化保证:(参见此处) 对于给定的 static ref NAME: TYPE = EXPR;,该宏会生成一个唯一类型,该类型实现 Deref<TYPE> 并将其存储在名为 NAME 的静态变量中。
这个包装类型并没有实现您的 trait,只有被包装的类型实现了。您需要调用 Deref,然后可能重新引用它才能获得一个 &Url,假设对 Url 的引用实现了您的 trait:
must_have_trait(&*EXAMPLE);

此外,仅使用裸静态变量会尝试将其移出静态位置(这将是一个非常糟糕的事情),因此您始终需要通过引用来使用它。

这个错误信息显示失败的类型,而不是值的名称!非常有趣。我认为编译器可以改进,让它将错误归咎于类型--我真的不明白为什么它要责备_value_,好像它是一种类型...谢谢 :) - Fredrick Brennan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接