Can You Implement INdividual Trait Functions for Generic Classes?
⚓ Rust 📅 2025-10-11 👤 surdeus 👁️ 3Hey 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
🏷️ Rust_feed