Skip to content

Introduce a lifetime generic to Visit in order to allow borrowing out specific parts of the valuable::Value during visits #97

Open
@nagisa

Description

@nagisa

Motivating use-case

My motivating use-case is to write down in tracing 0.2 a visitor that is able to borrow Event’s message. Doing so today is impossible even though lifetimes should conceptually mostly work out. This problem extends to valuable::Value and its Visit trait as well, so the remaining of the issue will focus on valuable.

So lets say we begin by writing a function as such (any mistakes mine, I basically sketched this out in a github comment field):

fn extract_message<'val>(value: &'val valuable::Value<'val>) -> &'val str {
    struct Visitor<'val> {
        message: &'val str
    }
    impl<'val> valuable::Visit for Visitor<'val> {
        fn visit_entry(&mut self, key: valuable::Value<'_>, value: valuable::Value<'_>) {
            match (key, value) {
                (valuable::Value::String("message"), valuable::Value::String(v)) => {
                    self.message = v; // ERROR: `'_` does not outlive `'val`.
                }
                _ => {}
            }
        }
    }

    let mut visitor = Visitor<'val> { message: "" };
    value.visit(&mut visitor); 
    visitor.message    
}

Rust today provides no way to tell that, '_ will outlive 'val here, and so users are forced to allocate in the implementation to extract the string data here.

Proposed solution

The proposed solution here is to introduce a lifetime to Visit such that Visit becomes like this:

pub trait Visit<'a> {
    fn visit_value(&mut self, value: Value<'a>);
    fn visit_named_fields(&mut self, named_values: &NamedValues<'a>) { ... }
    fn visit_unnamed_fields(&mut self, values: &[Value<'a>]) { ... }
    fn visit_primitive_slice(&mut self, slice: Slice<'a>) { ... }
    fn visit_entry(&mut self, key: Value<'a>, value: Value<'a>) { ... }
}

This gives implementers an optional opportunity to tie the lifetime of values to the lifetime of the visitors contents. Any implementors wanting the current behaviour can write the implementation as such:

// Unfortunately a breaking change ­– you cannot leave out the `<'_>`.
impl Visit<'_> for SomeVisitor { ... }

While implementations such as one presented in the motivating use-case above are also made possible:

impl<'val> valuable::Visit<'val> for Visitor<'val> {
    fn visit_entry(&mut self, key: valuable::Value<'val>, value: valuable::Value<'val>) {
        match (key, value) {
            (valuable::Value::String("message"), valuable::Value::String(v)) => {
                self.message = v; // A-OK!
            }
            _ => {}
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions