Constraining an associated type of a generic parameter

⚓ Rust    📅 2025-12-14    👤 surdeus    👁️ 6      

surdeus

Warning

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

Today I've found myself suddenly having to interact with traits with associated types, and I'm not 100% sure how to achieve what I need to achieve. Sorry that this is a bit long, but I'm not sure exactly how much context is needed for what I'm doing to make sense. There's my best-effort TL;DR at the bottom.

Background

I'm parsing a file using the logos crate, and if I encounter a problem, I'd like to pass up a error struct which contains the following information:

struct ParseError
{
    // The problematic token, or None if EOF
    token: Option<String>,

    // Where the token was encountered
    location: Range<usize>,

    // Programmer description, for context
    description: String,
}

To achieve this, I'd like to be able to create an error like so:

ParseError::from_token(lexer, "Something went wrong here")

My proposed function signature looks like:

// 'l is the lifetime that the lexer object has.
// Ctx is the enum type representing the tokens that are acceptable.
// It implements the Logos trait using #[derive(Logos)].
pub fn from_token<'l, Ctx>(lexer: &logos::Lexer<'l, Ctx>, description: &str)
    -> Self
    where Ctx: logos::Logos<'l>,

And the body of the function would look like this:

{
    return Self {
        token: Some(lexer.slice().to_owned()),
        location: lexer.span(),
        description: description.to_owned(),
    };
}

The issue I'm having is that lexer.slice().to_owned() needs quite precise constraints on the types/traits that my from_token() function uses.

First of all, using the naive signature above, the linter gives me an error regarding lexer.slice().to_owned():

the method to_owned exists for associated type <<Ctx as Logos<'_>>::Source
as Source>::Slice<'_>, but its trait bounds were not satisfied
the following trait bounds were not satisfied:
<<Ctx as Logos<'_>>::Source as Source>::Slice<'_>: Clone
which is required by <<Ctx as Logos<'_>>::Source as Source>::Slice<'_>: ToOwned

So there is a Slice type that needs constraining. Looking at the logos crate, the Source trait begins like this:

pub trait Source {
    /// A type this `Source` can be sliced into.
    type Slice<'a>: PartialEq + Eq + Debug
    where
        Self: 'a;

And Slice is later redefined like this:

impl Source for str {
    type Slice<'a> = &'a str;

So I understand that Source::Slice will refer to a &str when the underlying data source is a str.

However, this is where my current understanding reaches its limit. I'm able to eliminate the linter error about the Clone trait by basically copying and pasting the stated type chain as a trait constraint:

where
    Ctx: logos::Logos<'l>,
    <<Ctx as Logos<'l>>::Source as logos::Source>::Slice<'l>: Clone,

But this doesn't fix the whole problem. I now get a different linter error from lexer.slice().to_owned() that I don't know how to resolve:

mismatched types
expected struct String
found associated type <<Ctx as Logos<'_>>::Source as Source>::Slice<'_>
consider constraining the associated type <<Ctx as Logos<'_>>::Source as
Source>::Slice<'_> to String

My educated guess is that, without a constraint on the associated Slice type, the compiler cannot guarantee that the type will have a to_owned() function that returns a String, and therefore that I need some way to constrain the Slice type to be a &str in this function. I can see how the logos crate does this for its Source trait and the various impls of it, but I don't know how to do it locally in my function.

TL;DR

For a trait MyTrait that has an associated type MyTrait::A, how do I constrain MyTrait::A to a concrete type when writing my_function<T>() where T: MyTrait?

1 post - 1 participant

Read full topic

🏷️ Rust_feed