Почему ленивое статическое значение утверждает, что не реализует свойство, которое оно явно реализует?

В следующем коде (попытка сделать HTTP-запрос с использованием крейта reqwest) компилятор говорит, что мое значение SID_URI не реализует трейт PolyfillTryInto. Что тут происходит? reqwest::Url явно реализует закрытый трейт reqwest::into_url::PolyfillTryInto.

#[macro_use]
extern crate lazy_static;
extern crate reqwest;

static R_EMAIL: &str = "[email protected]";
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`

person Fredrick Brennan    schedule 05.01.2018    source источник


Ответы (1)


Компилятор вам не лжет, вы просто пропускаете важную деталь сообщения об ошибке. Вот самодостаточный пример:

#[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.

Этот тип-оболочка не реализует ваш трейт, только обернутый тип. Вам нужно будет вызвать Deref, а затем, возможно, повторно сослаться на него, чтобы получить &Url, предполагая, что ссылка на Url реализует ваш трейт:

must_have_trait(&*EXAMPLE);

Кроме того, использование голой статической переменной попытается переместить ее из статического местоположения (что было бы очень плохой вещью), поэтому вам всегда нужно использовать ее по ссылке.

person Shepmaster    schedule 05.01.2018
comment
В этом сообщении об ошибке указан тип ошибки, а не имя значения! Очень интересно. Я думаю, что компилятор можно было бы улучшить, чтобы обвинять тип - я действительно не понимал, почему он обвинял value, как если бы это был тип... спасибо :) - person Fredrick Brennan; 05.01.2018