1

I'm trying to implement a chainable query execution for TOML documents.

A Query is a thing that changes a TOML document and might return another Query object which shall be executed after itself. A Query which is executed gets the result of the previous query (if any).

The problem is that the return types are generic. A query might return a Vec<i64>, but its successor might return a String ... thus, the return type of one Query depends directly on the return type of its successor.

Here's my code so far:

extern crate either;
extern crate toml;

use either::Either;
use toml::Value;

type Result<T> = ::std::result::Result<T, ()>; // for simplicity

pub trait Query<Prev>
where
    Prev: Sized,
    Self: Sized,
{
    type Output: Sized;
    type Next: Query<Self::Output>;

    fn execute(&self, target: &mut Value, prev_result: Option<Prev>) -> Result<Self::Output>;

    fn next(self) -> Option<Self::Next>;
}

fn execute_query<Q, Prev>(
    doc: &mut Value,
    query: &Q,
    prev_result: Option<Prev>,
) -> Result<Either<Q::Output, <Q::Next as Query<Q::Output>>::Output>>
where
    Q: Query<Prev>,
{
    let result = query.execute(doc, prev_result)?;

    if let Some(next_query) = query.next() {
        let next_result: <Q::Next as Query<Q::Output>>::Output =
            match execute_query(doc, &next_query, Some(result)) {
                Ok(Either::Left(t)) => t,
                Ok(Either::Right(t)) => return Ok(Either::Right(t)), // error happens here
                Err(e) => return Err(e),
            };

        Ok(Either::Right(next_result))
    } else {
        Ok(Either::Left(result))
    }
}

(playground)

The error is that the return type is recursive (as the whole problem is recursive):

error[E0308]: mismatched types
  --> src/main.rs:37:65
   |
37 |                 Ok(Either::Right(t)) => return Ok(Either::Right(t)), // error happens here
   |                                                                 ^ expected type parameter, found associated type
   |
   = note: expected type `<<Q as Query<Prev>>::Next as Query<<Q as Query<Prev>>::Output>>::Output`
              found type `<<<Q as Query<Prev>>::Next as Query<<Q as Query<Prev>>::Output>>::Next as Query<<<Q as Query<Prev>>::Next as Query<<Q as Query<Prev>>::Output>>::Output>>::Output`

The title is not very expressive. I'm sorry about that, I don't know how to describe better.

4

1 回答 1

2

解决问题的整个方法都是错误的。我通过以下方式实现它:

  • Query特征提供了用于链接查询的功能。该函数返回一个Chain.
  • Chain类型Query通过执行第一个元素并将结果 (if Ok) 传递给第二个查询来实现。

使用这个可以解决问题:

use std::marker::PhantomData;

use toml::Value;
use error::Result;

pub trait Query<Prev>
where
    Prev: Sized,
    Self: Sized,
{
    type Output: Sized;

    fn execute(&self, target: &mut Value, prev_result: Option<Prev>) -> Result<Self::Output>;

    fn chain<Q>(self, other: Q) -> Chain<Self, Prev, Q>
    where
        Q: Query<Self::Output>,
    {
        Chain {
            first: self,
            _p: PhantomData,
            second: other,
        }
    }
}

pub struct Chain<A, P, B>
where
    A: Query<P>,
    B: Query<A::Output>,
    P: Sized,
{
    first: A,
    _p: PhantomData<P>,
    second: B,
}

impl<A, P, B> Query<P> for Chain<A, P, B>
where
    A: Query<P>,
    B: Query<A::Output>,
    P: Sized,
{
    type Output = B::Output;

    fn execute(&self, target: &mut Value, prev_result: Option<P>) -> Result<Self::Output> {
        let p = self.first.execute(target, prev_result)?;
        self.second.execute(target, Some(p))
    }
}

pub trait QueryExecutor {
    fn query<Q, T>(&mut self, q: &Q) -> Result<Q::Output>
    where
        Q: Query<T>;
}

impl QueryExecutor for Value {
    fn query<Q, T>(&mut self, q: &Q) -> Result<Q::Output>
    where
        Q: Query<T>,
    {
        q.execute(self, None as Option<T>)
    }
}

(包括测试的完整代码在这里

于 2018-01-17T19:26:02.720 回答