Iterating through strings with lifetime


#1

Hello,

This is something I’ve been wrestling with for a while. Basically, I have a function that takes a collection of strings and may return an error containing a reference to one of those strings:

#[derive(PartialEq, Debug)]
pub enum SomeError<'a> {
    BadThing(&'a str)
}
impl<'a> std::fmt::Display for SomeError<'a> { /* implementation */ }
impl<'a> std::error::Error for SomeError<'a> { /* implementation */ }

pub fn do_something<'a>(things: &[&'a str]) -> Result<(), SomeError<'a>> {
    Err(SomeError::BadThing(things[0]))
}

However, because it’s a public function and all I need to do with the collection is iterate through it, I’d rather not enforce the type of the collection:

pub fn do_something<'a, I, S>(things: I) -> Result<(), SomeError<'a, S>> 
    where I: std::iter::Iterator<Item=S> + Clone, // Clone because I want to make several passes
          S: AsRef<str> + 'a { /* implementation */ }

Despite S having a lifetime, the as_ref() function won’t return a reference with one because it’s not passed to AsRef's type parameter, so SomeError needs to use this generic type as well. But because of the Display and Error implementations for SomeError this requires some more trait restraints for S, eventually leading to this monstrosity:

#[derive(PartialEq, Debug)]
pub enum SomeError<'a, S>
    where S: AsRef<str> + std::marker::Reflect + std::fmt::Debug + 'a {
    
    BadThing(S)
}

impl<'a, S> std::fmt::Display for SomeError<'a, S> 
    where S: AsRef<str> + std::marker::Reflect + std::fmt::Debug + 'a { /* implementation */ }
impl<'a, S> std::error::Error for SomeError<'a, S> 
    where S: AsRef<str> + std::marker::Reflect + std::fmt::Debug + 'a { /* implementation */ }

pub fn do_something<'a, I, S>(things: I) -> Result<(), SomeError<'a, S>> 
    where I: std::iter::Iterator<Item=S> + Clone,
          S: AsRef<str> + std::marker::Reflect + std::fmt::Debug + 'a {
          
    Err(SomeError::BadThing(things.next().unwrap()))
}

I have two issues with this code: one is, that it still doesn’t compile. It gives the error parameter 'a is never used [E0392] for SomeError despite being a requirement for S. The second thing is, it really doesn’t feel like this is the way I should be doing this. Is there any, perhaps more “rustic”(?), way of doing this at all?

Thanks.


#2

This at least compiles: playpen. Requires nightly.


#3

You can also just use any to make it work on stable: http://is.gd/bTsowQ


#4

Using PhantomData is indeed what the compiler suggests and would’ve been fine if it could be a private member of a struct, but having it as part of a public enum element that would have to be documented to not get used is rather iffy.

Thinking about it some more, though, it may not make all that much sense to have an error have a borrowed value, because it would mean that in most cases the there’s no way of delaying the handling of the error. Few are going to mourn the clockcycles and memory used to copy the value and it makes things considerably easier and cleaner, so I’ve decided to go with that here.

Still, thanks for taking the time to reply.