I have this code (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
}
}
The compiler complains about 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`
When I write it explicitly, like this:
match connection_result {
Ok(c) => Ok(MSSQLConnection(c)),
Err(e) => Err(MSSQLConnectionManagerError(Errors::TiberiusError(e))),
}
Now it compiles successfully.
I do get the same compiler error though if I try this with L10, by returning Err(Self::Error(e))
.
Why doesn't this work as I expect?
Here is a minimal example that reproduces the same issue. Some type names were changed for clarity.
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)
}
}
The error emerges from attempting to use the associated type Connection
like an alias to the concrete type ConnectionId
, as defined by the implementation of Manager
. However, associated types do not behave entirely like a type alias. Even though we can construct a ConnectionId
(as it is a tuple struct, and we have visibility towards its member in this module), we cannot do that through the associated type Self::Connection
. What we could do is access other symbols defined by its constraints. For example, if we had this:
trait Manager {
type Connection: Default;
// ...
}
We would be able to call default
from Self::Connection
.
As such, changing the expression Ok(Self::Connection(c))
in the original example to Ok(MSSQLConnection(c)),
is the right way to fix this. In the event that you need to abstract the type away even at this step, you can constrain the associated type to a new trait providing the necessary construction methods.