Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rust cannot resolve trait bound for associated type #81436

Open
MarinPostma opened this issue Jan 27, 2021 · 0 comments
Open

Rust cannot resolve trait bound for associated type #81436

MarinPostma opened this issue Jan 27, 2021 · 0 comments
Labels
A-associated-items Area: Associated items such as associated types and consts. A-lifetimes Area: lifetime related A-traits Area: Trait system C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-types Relevant to the types team, which will review and decide on the PR/issue.

Comments

@MarinPostma
Copy link

I am trying to implement the Family design pattern as per this post by Luka and this one by Niko.

My goal is to abstract db transactions thanks to a Db trait that exposes read and write transaction. The Db trait have 2 associated types, Writer and Reader that define the operations that can be done on them. Ultimately, by being generic over the Reader and Writer types, I hope to be able to mock the actual db operations.

I tried this code:

This is what I have come up with:

Code

use std::marker::PhantomData;
use std::error::Error;

/// The `Db` trait represent an interaction with a transactional database.
pub trait Db {
    /// A writer representing the writable operation that can be operated on a database
    type Writer: for<'a> TxnFamily<'a>;
    /// A reader representing the readable operation that can be operated on a database
    type Reader: for<'a> TxnFamily<'a>;

    /// Provide the user with a writer representing a transaction. This function should commit if
    /// txn return Ok(_), an abort otherwise.
    fn write<'a, F, T, E>(&'a self, txn: F) -> anyhow::Result<Result<T, E>>
        where F: FnOnce(&mut <Self::Writer as TxnFamily<'a>>::Out) -> Result<T, E>,
              E: Error;

    /// Provide a reader representing a transaction.
    fn read<'a, F, T, E>(&'a self, txn: F) -> anyhow::Result<Result<T, E>>
        where F: FnOnce(&<Self::Reader as TxnFamily<'a>>::Out) -> Result<T, E>,
              E: Error;
}

pub trait TxnFamily<'a> { type Out; }

pub trait KvStoreDbWriter {
    fn put_key(&self, key: String, value: u64) -> anyhow::Result<()>;
}

impl<'a> KvStoreDbWriter for MapKvStoreDbWriter<'a> {
    fn put_key(&self, key: String, value: u64) -> anyhow::Result<()> {
        todo!()
    }
}

impl<'a> KvStoreDbReader for MapKvStoreDbReader<'a> {
    fn get_key(&self, key: &str) -> anyhow::Result<Option<u64>> {
        todo!()
    }
}

pub trait KvStoreDbReader {
    fn get_key(&self, key: &str) -> anyhow::Result<Option<u64>>;
}

// it doesn't matter wha this do either, but it is parametric
// over a lifetime for the length of the transaction.
pub struct MapKvStoreDbReader<'a>(PhantomData<&'a ()>);
pub struct MapKvStoreDbWriter<'a>(PhantomData<&'a ()>);


// What the MapKvStore actually do is irelevant,
// we want to be able to mock it a some point.
pub struct MapKvStoreDb;

pub struct MapKvStoreDbWriterTxn;
impl<'a> TxnFamily<'a> for MapKvStoreDbWriterTxn {
    type Out = MapKvStoreDbWriter<'a>;
}

pub struct MapKvStoreDbReaderTxn;
impl<'a> TxnFamily<'a> for MapKvStoreDbReaderTxn {
    type Out = MapKvStoreDbReader<'a>;
}

impl Db for MapKvStoreDb {
    type Writer = MapKvStoreDbWriterTxn;
    type Reader = MapKvStoreDbReaderTxn;
    fn write<'a, F, T, E>(&'a self, _txn: F) -> anyhow::Result<Result<T, E>>
        where F: FnOnce(&mut MapKvStoreDbWriter<'a>) -> Result<T, E>,
              E: Error {
                  todo!()
              }

    fn read<'a, F, T, E>(&'a self, _txn: F) -> anyhow::Result<Result<T, E>>
        where F: FnOnce(&MapKvStoreDbReader<'a>) -> Result<T, E>,
              E: Error {
                  todo!()
              }
}

struct KvStore<M> {
    store: M
}

impl<M> KvStore<M>
where
    M: Db,
    for<'a> <M::Reader as TxnFamily<'a>>::Out: KvStoreDbReader,
    for<'a> <M::Writer as TxnFamily<'a>>::Out: KvStoreDbWriter,
    
{
   fn new(store: M) -> Self {
       Self { store }
   } 
}

fn main() { 
    let store = MapKvStoreDb;
    let kv_store = KvStore::new(store);
}

and there on the playground https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=01bba28ff362a5f59da198b38a127d0e

I expect it to compile without error, since MapKvStoreDb implements Db<Writer = MapKvStoreDbWriterTxn, Reader = MapKvStoreDbReaderTxn>, and MapKvStoreDbReaderTxn implement TxnFamily whose Out implements KvStoreDbReader, and same for MapKvStoreDbWriterTxn.

Instead, I get this error:

   Compiling playground v0.0.1 (/playground)
error[E0599]: no function or associated item named `new` found for struct `KvStore<_>` in the current scope
  --> src/main.rs:99:29
   |
81 | struct KvStore<M> {
   | ----------------- function or associated item `new` not found for this
...
99 |     let kv_store = KvStore::new(store);
   |                             ^^^ function or associated item not found in `KvStore<_>`
   |
   = note: the method `new` exists but the following trait bounds were not satisfied:
           `<_ as TxnFamily<'a>>::Out: KvStoreDbReader`
           `<_ as TxnFamily<'a>>::Out: KvStoreDbWriter`
@MarinPostma MarinPostma added the C-bug Category: This is a bug. label Jan 27, 2021
@ChrisDenton ChrisDenton added the needs-triage-legacy Old issue that were never triaged. Remove this label once the issue has been sufficiently triaged. label Jul 16, 2023
@fmease fmease added A-traits Area: Trait system T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-types Relevant to the types team, which will review and decide on the PR/issue. A-lifetimes Area: lifetime related A-associated-items Area: Associated items such as associated types and consts. and removed needs-triage-legacy Old issue that were never triaged. Remove this label once the issue has been sufficiently triaged. labels Jan 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-associated-items Area: Associated items such as associated types and consts. A-lifetimes Area: lifetime related A-traits Area: Trait system C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-types Relevant to the types team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

3 participants