首页 > 解决方案 > 从方法内的模式匹配返回时,在当前范围内找不到关联类型

问题描述

我有这个代码(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)).

为什么这不像我预期的那样工作?

标签: compiler-errorsrustpattern-matchingassociated-types

解决方案


这是一个重现相同问题的最小示例。为清楚起见,更改了某些类型名称。

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(因为它是一个元组结构,并且我们在这个模块中可以看到它的成员),但我们不能通过关联的 type 来做到这一点Self::Connection。我们可以做的是访问由其约束定义的其他符号。例如,如果我们有这个:

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

我们将能够从default调用Self::Connection

因此,Ok(Self::Connection(c))将原始示例中的表达式更改为Ok(MSSQLConnection(c)),是解决此问题的正确方法。如果您需要在此步骤中抽象出类型,您可以将关联的类型约束为提供必要构造方法的新特征。


推荐阅读