У меня есть этот код (bb42e59):

pub extern crate r2d2;
pub extern crate tiberius;
pub extern crate futures;

use self::tiberius::BoxableIo;
use self::futures::prelude::*;

use core::fmt::Debug;
#[allow(unused_imports)]
use std::error::Error;

type TiberiusConnection = self::tiberius::SqlConnection<Box<BoxableIo>>;

#[derive(Debug)]
pub enum Errors { TiberiusError(tiberius::Error) }

#[derive(Debug)]
pub struct MSSQLConnectionManagerError(Errors);

impl ::std::error::Error for MSSQLConnectionManagerError {
    fn description(&self) -> &str {
        match self.0 {
            Errors::TiberiusError(ref e) => {
                match e {
                    tiberius::Error::Io(e) => e.description(),
                    tiberius::Error::Protocol(msg) => &msg,
                    tiberius::Error::Encoding(msg) => &msg,
                    tiberius::Error::Conversion(msg) => &msg,
                    tiberius::Error::Utf8(e) => e.description(),
                    tiberius::Error::Utf16(e) => e.description(),
                    tiberius::Error::ParseInt(e) => e.description(),
                    // TODO: parse the server token if possible and report the actual error that occurred, like invalid login, etc.
                    tiberius::Error::Server(_) => "TDS token error occurred! When connecting, most often an invalid login.",
                    tiberius::Error::Canceled => "Canceled!",
                }
            }
        }
    }
}

impl ::std::fmt::Display for MSSQLConnectionManagerError {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        match self.0 { Errors::TiberiusError(ref e) => e.fmt(f), }
    }
}

pub struct MSSQLConnection(TiberiusConnection);

pub struct MSSQLConnectionManager { connection_string: String }

impl MSSQLConnectionManager {
    pub fn new(connection_string: String) -> MSSQLConnectionManager {
        MSSQLConnectionManager { connection_string }
    }

    pub fn from_env() -> Result<MSSQLConnectionManager, ::std::env::VarError> {
        let connection_string = ::std::env::var("MSSQL_CONNECTION_STRING")?;
        Ok(MSSQLConnectionManager { connection_string })
    }
}

impl r2d2::ManageConnection for MSSQLConnectionManager {
    type Connection = MSSQLConnection;
    type Error = MSSQLConnectionManagerError;

    fn connect(&self) -> Result<Self::Connection, Self::Error> {
        let connection_result = TiberiusConnection::connect(&self.connection_string)
            .and_then(|c| Ok(c)).wait();
        match connection_result {
            Ok(c) => Ok(MSSQLConnection(c)),
            Err(e) => Err(MSSQLConnectionManagerError(Errors::TiberiusError(e))),
        }
    }

    fn is_valid(&self, _conn: &mut Self::Connection) -> Result<(), Self::Error> {
        // TODO: Fix this quick and dirty implementation by checking the result of a simple query.
        Ok(())
    }

    fn has_broken(&self, _conn: &mut Self::Connection) -> bool {
        // TODO: Fix this quick and dirty implementation by checking underlying TCP socket state.
        false
    }
}

Компилятор жалуется на Ok(c) => Ok(Self::Connection(c)),:

error[E0599]: no associated item named `Connection` found for type `persistence::mssql::MSSQLConnectionManager` in the current scope
  --> src/persistence/mssql.rs:77:25
   |
56 | pub struct MSSQLConnectionManager { connection_string: String }
   | --------------------------------- associated item `Connection` not found for this
...
77 |             Ok(c) => Ok(Self::Connection(c)),
   |                         ^^^^^^^^^^^^^^^^ associated item not found in `persistence::mssql::MSSQLConnectionManager`

Когда я пишу это явно, вот так:

match connection_result {
    Ok(c) => Ok(MSSQLConnection(c)),
    Err(e) => Err(MSSQLConnectionManagerError(Errors::TiberiusError(e))),
}

Теперь он успешно компилируется. Я получаю ту же ошибку компилятора, если попробую это с L10, вернув Err(Self::Error(e)).

Почему это не работает так, как я ожидал?

-1
Paul-Sebastian Manole 4 Окт 2018 в 19:31

1 ответ

Лучший ответ

Вот минимальный пример, воспроизводящий ту же проблему. Некоторые названия типов изменены для ясности.

trait Manager {
    type Connection;

    fn connect(&self) -> Self::Connection;
}

pub struct ConnectionId(usize);

pub struct FooManager;

impl Manager for FooManager {
    type Connection = ConnectionId;

    fn connect(&self) -> Self::Connection {
        Self::Connection(5)
    }
}

Ошибка возникает из-за попытки использовать связанный тип Connection как псевдоним для конкретного типа ConnectionId, как определено реализацией Manager. Однако связанные типы не ведут себя полностью как псевдоним типа. Несмотря на то, что мы можем построить ConnectionId (поскольку это кортежная структура и мы видим ее член в этом модуле), мы не можем сделать это через связанный тип Self::Connection. Что мы могли сделать, так это получить доступ к другим символам, определяемым его ограничениями. Например, если бы у нас было это:

trait Manager {
    type Connection: Default;
    // ...
}

Мы сможем позвонить default из Self::Connection.

Таким образом, изменение выражения Ok(Self::Connection(c)) в исходном примере на Ok(MSSQLConnection(c)), - правильный способ исправить это. В случае, если вам нужно абстрагироваться от типа даже на этом этапе, вы можете ограничить связанный тип новым признаком, предоставляя необходимые методы построения.

3
E_net4 the downvoter 4 Окт 2018 в 17:45