From 5e19ca5cf4e94a89a932731f3cb37d2bf48e25a9 Mon Sep 17 00:00:00 2001 From: David Crossman <22548398+DavidCrossman@users.noreply.github.com> Date: Thu, 18 Jun 2026 20:31:10 +0100 Subject: [PATCH 1/2] Relax `Logos::Error` trait bounds --- examples/array_language.rs | 11 ++--------- examples/custom_error.rs | 4 +--- src/lib.rs | 3 +-- tests/src/lib.rs | 1 + 4 files changed, 5 insertions(+), 14 deletions(-) diff --git a/examples/array_language.rs b/examples/array_language.rs index 2cd82999..baeb9b57 100644 --- a/examples/array_language.rs +++ b/examples/array_language.rs @@ -20,16 +20,10 @@ use std::path::Path; /* ANCHOR: error_type */ /// Token error type, tied to the lifetime of the source. -#[derive(Default, Debug, Clone, PartialEq)] enum LexingError<'s> { UnknownSymbol(&'s str), - InvalidInteger { - err: ParseIntError, - source: &'s str, - }, + InvalidInteger { err: ParseIntError, source: &'s str }, UnknownVariable(&'s str), - #[default] - Other, } /* ANCHOR_END: error_type */ @@ -41,7 +35,6 @@ impl Display for LexingError<'_> { write!(f, "int error in source `{source}`: {err}") } Self::UnknownVariable(s) => write!(f, "unknown variable `{s}`"), - Self::Other => write!(f, "unknown error"), } } } @@ -51,7 +44,7 @@ type Environment = HashMap>; /* ANCHOR_END: environment */ /* ANCHOR: callbacks */ -/// Parse lexer slice as an i128 +/// Parse lexer slice as an `i128` fn number_callback<'s>(lex: &mut Lexer<'s, Token>) -> Result> { let source = lex.slice(); let res = source.parse(); diff --git a/examples/custom_error.rs b/examples/custom_error.rs index 71bf799f..09babe67 100644 --- a/examples/custom_error.rs +++ b/examples/custom_error.rs @@ -11,12 +11,10 @@ use logos::Logos; use std::num::ParseIntError; -#[derive(Default, Debug, Clone, PartialEq)] +#[derive(Debug, PartialEq)] enum LexingError { InvalidInteger(String), NonAsciiCharacter(char), - #[default] - Other, } /// Error type returned by calling `lex.slice().parse()` to u8. diff --git a/src/lib.rs b/src/lib.rs index c493f7f9..0a18edac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,7 +26,6 @@ extern crate core; -use core::fmt::Debug; #[cfg(feature = "export_derive")] pub use logos_derive::Logos; @@ -53,7 +52,7 @@ pub trait Logos<'source>: Sized { /// Error type returned by the lexer. This can be set using /// `#[logos(error = MyError)]`. Defaults to `()` if not set. - type Error: Default + Clone + PartialEq + Debug + 'source; + type Error: 'source; /// The heart of Logos. Called by the `Lexer`. The implementation for this function /// is generated by the `logos-derive` crate. diff --git a/tests/src/lib.rs b/tests/src/lib.rs index 1d4dfd38..00a37e5e 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -15,6 +15,7 @@ pub fn assert_lex<'a, Token>( ) where Token: Logos<'a> + fmt::Debug + PartialEq, Token::Extras: Default, + Token::Error: fmt::Debug + PartialEq, { let mut lex = Token::lexer(source); From 67e4554a4f9e873664d29ad4c3f2864674f93e52 Mon Sep 17 00:00:00 2001 From: David Crossman <22548398+DavidCrossman@users.noreply.github.com> Date: Thu, 18 Jun 2026 20:50:22 +0100 Subject: [PATCH 2/2] Update book with relaxed error bounds --- book/src/attributes/logos.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/book/src/attributes/logos.md b/book/src/attributes/logos.md index b2d70672..7996c512 100644 --- a/book/src/attributes/logos.md +++ b/book/src/attributes/logos.md @@ -32,9 +32,9 @@ For more details about extras, read the [eponym section](../extras.md). By default, **Logos** uses `()` as the error type, which means that it doesn't store any information about the error. -This can be changed by using `#[logos(error = ErrorType)]` attribute on the enum. -The type `ErrorType` can be any type that implements `Clone`, `PartialEq`, -`Default` and `From` for each callback's error type. +This can be changed by using the `#[logos(error = ErrorType)]` attribute on the enum. +The type `ErrorType` can be any type that implements `From` for each callback's error type. +If no error callback is provided, `ErrorType` must implement `Default` as well. Here is an example using a custom error type: @@ -46,10 +46,7 @@ You can add error variants to `LexingError`, and implement `From` for each error type `E` that could be returned by a callback. See [callbacks](../callbacks.md). -`ErrorType` must implement the `Default` trait because invalid tokens, i.e., -literals that do not match any variant, will produce `Err(ErrorType::default())`. - -Alternatively, you can provide a callback with the alternate syntax +You can provide a callback with the syntax `#[logos(error(ErrorType, callback = ...))]`, which allows you to include information from the lexer such as the span where the error occurred: @@ -64,6 +61,10 @@ enum Token { } ``` +If you do not provide a callback, then invalid tokens, i.e., +literals that do not match any variant, will produce `Err(ErrorType::default())`. +This requires `ErrorType` to implement the `Default` trait. + ## Specifying path to logos You can force the derive macro to use a different path to `Logos`' crate