Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazy context #605

Open
2 tasks done
9999years opened this issue Oct 14, 2024 · 5 comments
Open
2 tasks done

Lazy context #605

9999years opened this issue Oct 14, 2024 · 5 comments
Labels
C-enhancement Category: Raise on the bar on expectations

Comments

@9999years
Copy link
Contributor

Please complete the following tasks

winnow version

master

Describe your use case

I'd like to add context to parsers without immediately allocating a Vec for them

Describe the solution you'd like

Would it be possible to have a .context_from(|| vec!["context1", "context2"]) function which only computes the context on error branches? Ideally this would be a middle-ground for performance between allocating the contexts up-front and omitting them entirely.

Alternatives, if applicable

Not sure! I'm interested to hear discussion.

Additional Context

See #421.

@9999years 9999years added the C-enhancement Category: Raise on the bar on expectations label Oct 14, 2024
@epage
Copy link
Collaborator

epage commented Oct 14, 2024

Could you share your error type and context type?

@9999years
Copy link
Contributor Author

I'm using PResult<ContextError<StrContext>>, but I'll probably create my own context type like this so I can dynamically construct informative parse context messages:

#[derive(Debug, Clone)]
pub enum ParseContext {
    Description(Cow<'static, str>),
    Expected(Cow<'static, str>),
}

@epage
Copy link
Collaborator

epage commented Oct 16, 2024

ContextError doesn't immediately allocate but does only pushes on error.

I'm confused by the example because you don't pass a Vec to context but a single context.

@9999years
Copy link
Contributor Author

I'm confused by the example because you don't pass a Vec to context but a single context.

Yeah, sorry, to be clear I mean a method which computes multiple contexts up-front.

ContextError doesn't immediately allocate but does only pushes on error.

You're right, thank you. I've looked at the code a bit more. But the Parser::context method does take the context up-front, which means that the cost of computing the context itself is always paid. Not much of an issue for &'static str contexts, but for anything else it could be expensive:

winnow/src/parser.rs

Lines 646 to 660 in 8674ed2

/// If parsing fails, add context to the error
///
/// This is used mainly to add user friendly information
/// to errors when backtracking through a parse tree.
#[doc(alias = "labelled")]
#[inline(always)]
fn context<C>(self, context: C) -> Context<Self, I, O, E, C>
where
Self: core::marker::Sized,
I: Stream,
E: AddContext<I, C>,
C: Clone + crate::lib::std::fmt::Debug,
{
Context::new(self, context)
}

Maybe I'm just holding it wrong? Not sure.

@epage
Copy link
Collaborator

epage commented Oct 23, 2024

It would help if you gave a more complete example of what you are trying to accomplish.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-enhancement Category: Raise on the bar on expectations
Projects
None yet
Development

No branches or pull requests

2 participants