Can You Implement INdividual Trait Functions for Generic Classes?

⚓ Rust    📅 2025-10-11    👤 surdeus    👁️ 3      

surdeus

Hey Rustacians!

So, I'm still programming my SWC plugin and stumbled across the situation where I have a generic struct NodeSegment with the type of the underlying node (either an expression or a type, maybe more to come) being generic and want to implement one individual trait function is_local for all generic NodeSegment type assignments in the same way.

With NamedNodes get_context function retrieving the validity scope of the underlying node and with NodeSegment having a list of local_contexts, it'd be fairly easy to implement this by just checking whether local_contexts contains get_context().

However, I can't seem to write a common implementation for this individual function like so:

impl<'a, T: NamedNode<'a>> NameSegment<'a> for NodeSegment<'a, T> {
    fn is_local(&self) -> bool {
        match self.node.get_context() {
            Some(context) => self.local_contexts.contains(context),
            None => false,
        }
    }
}

but instead have to write this function implementation two times; once for impl<'a> NameSegment<'a> for NodeSegment<'a, NamedExpr<'a>> and once for impl<'a> NameSegment<'a> for NodeSegment<'a, NamedType<'a>>.

To overcome this, I just moved is_local to the impl<'a, T: NamedNode<'a>> NodeSegment<'a, T> part and, from both aforementioned implementations, just call Self::is_local().

Is there really no more smooth way to overcome this?
Am I doing this correctly? Because somehow, it feels kinda wrong...?

Here's the stripped down version of my current code:

use swc_core::{
    common::SyntaxContext,
    ecma::ast::{Expr, MemberExpr, ParenExpr, TsAsExpr, TsNonNullExpr, TsType},
};

pub enum NameofError {}

/// Represents either success or a [`NameofError`].
type NameofResult<'a, T> = Result<T, NameofError>;

/// Represents the format of a segment.
pub enum SegmentFormat {
    Ident(String),
}

/// Represents a node which has a name.
pub trait NamedNode<'a> {
    /// Gets the syntax context of the node.
    fn get_context(&self) -> Option<&'a SyntaxContext>;
}

/// Represents an expression which has a name.
pub enum NamedExpr<'a> {
    /// Indicates an expression.
    Expr(&'a Expr),
}

impl<'a> NamedNode<'a> for NamedExpr<'a> {
    fn get_context(&self) -> Option<&'a SyntaxContext> {
        todo!()
    }
}

/// Represents a type which has a name.
pub enum NamedType<'a> {
    /// Indicates a type.
    Type(&'a TsType),
}

impl<'a> NamedNode<'a> for NamedType<'a> {
    fn get_context(&self) -> Option<&'a SyntaxContext> {
        todo!()
    }
}

/// Represents the segment of a name.
pub trait NameSegment<'a> {
    /// Checks whether the current segment is a variable which was introduced in a `nameof` function expression.
    fn is_local(&self) -> bool;
    /// Gets the format of the segment.
    fn get_format(&self) -> NameofResult<'a, SegmentFormat>;
}

/// Represents the segment of a chain of nodes.
pub struct NodeSegment<'a, T> {
    local_contexts: &'a Vec<SyntaxContext>,
    /// The node of the segment.
    node: T,
}

impl<'a, T: NamedNode<'a>> NodeSegment<'a, T> {
    fn is_local(&self) -> bool {
        match self.node.get_context() {
            Some(context) => self.local_contexts.contains(context),
            None => false,
        }
    }
}

impl<'a> NodeSegment<'a, NamedExpr<'a>> {
    /// Unwraps the expression nested inside the specified `expr`.
    fn unwrap_expr<'b>(expr: &'b Expr) -> &'b Expr {
        match expr {
            Expr::Paren(ParenExpr { expr, .. })
            | Expr::TsNonNull(TsNonNullExpr { expr, .. })
            | Expr::TsAs(TsAsExpr { expr, .. }) => {
                return expr;
            }
            expr => expr,
        }
    }

    fn get_member_format<'b>(&self, member: &'b MemberExpr) -> NameofResult<'b, SegmentFormat> {
        todo!()
    }
}

impl<'a> NameSegment<'a> for NodeSegment<'a, NamedExpr<'a>> {
    fn is_local(&self) -> bool {
        Self::is_local(&self)
    }

    fn get_format(&self) -> NameofResult<'a, SegmentFormat> {
        Ok(SegmentFormat::Ident(match self.node {
            NamedExpr::Expr(expr) => match Self::unwrap_expr(expr) {
                Expr::Member(member) => return self.get_member_format(member),
                _ => todo!(),
            },
        }))
    }
}

impl<'a> NameSegment<'a> for NodeSegment<'a, NamedType<'a>> {
    fn is_local(&self) -> bool {
        Self::is_local(&self)
    }

    fn get_format(&self) -> NameofResult<'a, SegmentFormat> {
        todo!()
    }
}

3 posts - 2 participants

Read full topic

🏷️ Rust_feed