Repository: pest-parser/pest (language: rust)

Issue:
Breaking change from 2.1.3 to 2.2.1 due to new RuleType bound on Error types
**Describe the bug**
In commit 5b807be20933a6b8c5b2484639829d0ef7ece2ff, `Error<R>` was changed to `Error<R: RuleType>`, which is a breaking change. As far as I can see, this also extends to `ErrorVariant`.

**To Reproduce**
Compiles with Pest 2.1.3, but not with 2.2.1:

```rust
fn inspect_pest_error<R>(err: &pest::error::Error<R>) {
}
```

To make it compile with 2.2.1, a bound on `R` must be added:

```rust
fn inspect_pest_error<R: pest::RuleType>(err: &pest::error::Error<R>) {
}
```

**Additional context**
For me personally, this is not a huge deal. My code didn't require any bigger changes to make it compile again. But it can certainly be surprising.



Discussion / hints:
@dennis-hamester thanks for the report.

@pest-parser/maintainers @pest-parser/triage any thoughts on how to resolve it? Revert that commit and release a new version? Or keep it as it is and be more careful next time (E.g. with https://crates.io/crates/cargo-semver-checks)? Or release it as v3.0.0 (despite it not being the "mythical pest3" with the new grammar etc.)?
I think leaving it as is and adding a check is probably reasonable, perhaps with a note on the release?
I could go either way. I agree it's not a huge deal. Maybe a comment on the offending type would help?

Note that, ultimately, we'll probably need to go to v3.0.0 before "mythical pest3", so the sooner we get that over and done with, the better?

Fix patch:
diff --git a/pest/src/error.rs b/pest/src/error.rs
index c2ab8066..a83e23a4 100644
--- a/pest/src/error.rs
+++ b/pest/src/error.rs
@@ -16,6 +16,7 @@ use alloc::string::String;
 use alloc::string::ToString;
 use alloc::vec::Vec;
 use core::cmp;
+use core::fmt;
 use core::mem;
 
 use crate::position::Position;
@@ -25,8 +26,7 @@ use crate::RuleType;
 /// Parse-related error type.
 #[derive(Clone, Debug, Eq, Hash, PartialEq)]
 #[cfg_attr(feature = "std", derive(thiserror::Error))]
-#[cfg_attr(feature = "std", error("{}", self.format()))]
-pub struct Error<R: RuleType> {
+pub struct Error<R> {
     /// Variant of the error
     pub variant: ErrorVariant<R>,
     /// Location within the input string
@@ -41,9 +41,8 @@ pub struct Error<R: RuleType> {
 /// Different kinds of parsing errors.
 #[derive(Clone, Debug, Eq, Hash, PartialEq)]
 #[cfg_attr(feature = "std", derive(thiserror::Error))]
-pub enum ErrorVariant<R: RuleType> {
+pub enum ErrorVariant<R> {
     /// Generated parsing error with expected and unexpected `Rule`s
-    #[cfg_attr(feature = "std", error("parsing error: {}", self.message()))]
     ParsingError {
         /// Positive attempts
         positives: Vec<R>,
@@ -51,7 +50,6 @@ pub enum ErrorVariant<R: RuleType> {
         negatives: Vec<R>,
     },
     /// Custom error with a message
-    #[cfg_attr(feature = "std", error("{}", self.message()))]
     CustomError {
         /// Short explanation
         message: String,
@@ -520,6 +518,21 @@ impl<R: RuleType> ErrorVariant<R> {
     }
 }
 
+impl<R: RuleType> fmt::Display for Error<R> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", self.format())
+    }
+}
+
+impl<R: RuleType> fmt::Display for ErrorVariant<R> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            ErrorVariant::ParsingError { .. } => write!(f, "parsing error: {}", self.message()),
+            ErrorVariant::CustomError { .. } => write!(f, "{}", self.message()),
+        }
+    }
+}
+
 fn visualize_whitespace(input: &str) -> String {
     input.to_owned().replace('\r', "␍").replace('\n', "␊")
 }


Test patch: