-
-
Save sgrif/48bf08a27b3da35a44ac085cada0622c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pub trait DistinctDsl { | |
type Output; | |
fn distinct(self) -> Self::Output; | |
} | |
#[derive(Default)] | |
pub struct SelectStatement<D = NoDistinctClause, FU = NoForUpdateClause> { | |
distinct: D, | |
for_update: FU, | |
} | |
impl<FU> SelectStatement<NoDistinctClause, FU> { | |
pub fn for_update(self) -> SelectStatement<NoDistinctClause, ForUpdateClause> { | |
SelectStatement { | |
distinct: self.distinct, | |
for_update: ForUpdateClause, | |
} | |
} | |
} | |
pub struct DistinctClause; | |
#[derive(Default)] | |
pub struct NoDistinctClause; | |
pub struct ForUpdateClause; | |
#[derive(Default)] | |
pub struct NoForUpdateClause; | |
impl<D> DistinctDsl for SelectStatement<D> { | |
type Output = SelectStatement<DistinctClause>; | |
fn distinct(self) -> Self::Output { | |
SelectStatement { | |
distinct: DistinctClause, | |
for_update: self.for_update, | |
} | |
} | |
} | |
pub trait AsQuery { | |
type Query; | |
fn as_query(self) -> Self::Query; | |
} | |
impl<D, FU> AsQuery for SelectStatement<D, FU> { | |
type Query = Self; | |
fn as_query(self) -> Self::Query { | |
self | |
} | |
} | |
pub trait SelectStatementDefinitelyDoesntImplementMe: AsQuery {} | |
// This is the thing that causes problems. Since `DistinctDsl` is implemented only when the `FU` | |
// slot is a specific type, someone could theoretically write `impl<D> | |
// SelectStatementDefinitelyDoesntImplementMe for SelectStatement<D, MyLocalType>`, which *would* | |
// make this infinitely recursive. However, that case gets evaluated even when we are looking at | |
// the concrete type `SelectStatement<NoDistinctClause, ForUpdateClause>, which we can definitively | |
// say *does not* implement `SelectStatementDefinitelyDoesntImplementMe` | |
impl<T> DistinctDsl for T | |
where | |
T: SelectStatementDefinitelyDoesntImplementMe, | |
T::Query: DistinctDsl, | |
{ | |
type Output = <T::Query as DistinctDsl>::Output; | |
fn distinct(self) -> Self::Output { | |
self.as_query().distinct() | |
} | |
} | |
pub fn stuff() { | |
// UFCS is important here. If we use the method syntax, it just resolves to the method not | |
// existing (this discrepancy is incredibly annoying, the overflow should either not be masked | |
// there, or we should consistently treat overflow as "this impl doesn't apply") | |
DistinctDsl::distinct(SelectStatement::default().for_update()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment