Can You Implement INdividual Trait Functions for Generic Classes?

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

surdeus

Warning

This post was published 100 days ago. The information described in this article may have changed.

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