rj76
rj76

Reputation: 11

Why can't I make a generic struct and a trait's generic method work together?

I have a question about Rust traits. I have the following:

pub trait SelectAsDsl: Sized {
    type Output;

    fn select_as<SQ>(self, subquery: SQ, alias: String) -> Self
    where
        SQ: AsExpression<SQ> + SingleValue + SqlType;
}

#[derive(QueryId, Debug, Clone)]
pub struct SelectAs<SQ> {
    subquery: SQ,
    alias: String,
}

impl<T> SelectAsDsl for SelectAs<T> {
    type Output = Self;

    fn select_as<SQ>(self, subquery: SQ, alias: String) -> Self
    where
        SQ: AsExpression<SQ> + SingleValue,
    {
        SelectAs {
            subquery,
            alias,
            ..self
        }
    }
}

This gives an error "expected type parameter T, found type parameter SQ" in the trait implementation when returning the struct, but when naming them the same, the compiler complains that the name is already in use.

When changing it to create a void struct and returning self, the compiler gives no error.

impl<T> SelectAsDsl for SelectAs<T> {
    type Output = Self;

    fn select_as<SQ>(self, subquery: SQ, alias: String) -> Self
    where
        SQ: AsExpression<SQ> + SingleValue,
    {
        let _ = SelectAs { subquery, alias };
        self
    }
}

How is this caused and how should I fix this?

Upvotes: 1

Views: 547

Answers (1)

Chayim Friedman
Chayim Friedman

Reputation: 71440

There is no connection between the struct's SQ and the trait method's SQ (except they share the same name, of course). The fact that they share the same name is just confusing. select_as() should return Self, which is SelectAs<T> in the context of the impl you showed, but it returns SelectAs<SQ>.

The fix is obviously to make it returning SelectAs<T>. Since it needs subquery to be of type T, that means we need to declare it so. This is a bit problematic since the trait (where we declare it) does not have access to T. So we should declare T (or SQ) as a parameter of the trait instead of the method (maybe even an associated type):

pub trait SelectAsDsl<T>: Sized {
    type Output;
    fn select_as<T>(self, subquery: T, alias: String) -> Self
    where T: AsExpression<T> + SingleValue + SqlType;
}

impl<T> SelectAsDsl<T> for SelectAs<T>
{
    type Output = Self;

    fn select_as(self, subquery: T, alias: String) -> Self
    where T: AsExpression<T> + SingleValue + SqlType
    {
        SelectAs {
            subquery,
            alias,
            ..self
        }
    }
}

Upvotes: 1

Related Questions