Lifetime in tuple of types?


#1

TL;DR:
I want to be able to define this tuple type once and reuse it where needed:

(&'a Rc<HilbertNumber>, &'a Rc<Point2>)

Details:
In my consuming iterator (“for h in hmap”), I can do something like this:

    // #[derive(Debug)]
    pub type HilbertMapItem = (HilbertNumber, Point2);

    impl IntoIterator for HilbertMap<HilbertNumber, Point2> {
        type Item = HilbertMapItem;
        type IntoIter = HilbertMapIntoIterator;
        fn into_iter(self) -> Self::IntoIter {
            HilbertMapIntoIterator {
                map: self.left_to_right.into_iter()
            }
        }
    }

    pub struct HilbertMapIntoIterator {
        map: IntoIter<Rc<HilbertNumber>, Rc<Point2>>
    }

    impl Iterator for HilbertMapIntoIterator {
        type Item = HilbertMapItem;

        fn next(&mut self) -> Option<HilbertMapItem> {
            let next_hilbert = self.map.next();
            let result = match next_hilbert {
                Some(x) => Some(
                    (
                        Rc::try_unwrap(x.0).unwrap(),
                        Rc::try_unwrap(x.1).unwrap()
                    )
                ),
                None => None
            };
            result
        }
    }

And that’s all fine and dandy.

But when I’m making a reference iterator (“for h in **&**hmap”), I can’t define an Item type that can be reused, and I am forced to explicitly rewrite the full definition where I am using it multiple times. I know this has to do with the 'a lifetime; however, shouldn’t this be possible to do without duplicating my code?

What I want to do produces an error:

pub type HilbertMapItemRef = (&'a Rc<HilbertNumber>, &'a Rc<Point2>);
error[E0261]: use of undeclared lifetime name `'a`
  --> hilbert_map/mod.rs:25:29
   |
25 | pub type HilbertItemRef = (&'a Rc<HilbertNumber>, &'a Rc<Point2>);
   |                             ^^ undeclared lifetime

error[E0261]: use of undeclared lifetime name `'a`
  --> hilbert_map/mod.rs:25:52
   |
25 | pub type HilbertItemRef = (&'a Rc<HilbertNumber>, &'a Rc<Point2>);
   |                                                    ^^ undeclared lifetime

In order to work, I have to do this:

    impl<'a> IntoIterator for &'a HilbertMap<HilbertNumber, Point2> {
        type Item = (&'a Rc<HilbertNumber>, &'a Rc<Point2>);
        type IntoIter = HilbertMapIter<'a>;
        fn into_iter(self) -> Self::IntoIter { 
            HilbertMapIter {
                map: self.left_to_right.iter()
            }
        }
    }

    pub struct HilbertMapIter<'a> {
        map: Iter<'a, Rc<HilbertNumber>, Rc<Point2>>
    }

    impl<'a> Iterator for HilbertMapIter<'a> {
        type Item = (&'a Rc<HilbertNumber>, &'a Rc<Point2>);

        fn next(&mut self) -> Option<(&'a Rc<HilbertNumber>, &'a Rc<Point2>)> {
            let next_hilbert = self.map.next();
            let result = match next_hilbert {
                Some(x) => Some(
                    (
                        x.0,
                        x.1
                    )
                ),
                None => None
            };
            result   
        }
    }

Is there any way to incorporate the 'a lifetime parameter into a reusable definition instead of rewriting it every time?


#2

I just skimmed over your post - so it’s possible that I’m not answering every question. But you have to introduce the lifetime 'a in this statement like this

pub type HilbertMapItemRef<'a> = (&'a Rc<HilbertNumber>, &'a Rc<Point2>);

Just put it behind the name, like you did with the struct. :slight_smile:

PS: You’ll also get syntax-highlighting in this forum if you annotate code blocks like this

```rust
pub type ...
```

#3

There isn’t.

A lifetime parameter is like a type parameter - when you use one of your types it is instantiated with a particular lifetime, just like when you create a Vec<T> it is instantiated with whatever type you put into it. You need the parameter to track that instantiation process.

We’ve been considering a syntax though where if all the lifetimes are going to be the same lifetime, instead of using 'a everwhere you can just use ' So this would be more like:

pub struct HilbertMapIter<'> {
    map: Iter<', Rc<HilbertNumber>, Rc<Point2>>
}

impl Iterator for HilbertMapIter<'> {
    type Item = (&Rc<HilbertNumber>, Rc<Point2>);
    ...
}

The details of that idea haven’t been fully worked out.


#4

FWIW I don’t like that. It gains nothing and is just yet another thing to discover.


#5

I couldn’t find an RFC or internals discussion for this proposal. Is there any?

I do like the idea, that explicit lifetimes would only be needed for disambiguation.

It would be nice to have this syntax more symmetrical with elision in “direct references”: &'a -> &, where the whole 'a disappears. On the other hand eliding that much would make it hard to reason locally about inner lifetimes of these structs. So eliding only a, but keeping ', seems like a tradeoff. This need for a tradeoff is the only thing that bothers me a bit, but I think I’ll get used to it. :slight_smile:


#6

It’s not far enough along to have had one yet; it’s just an idea various people have had.