Soni
October 7, 2022, 8:46pm
1
Currently we have this:
fn for_names<F: FnMut(&'pat str)>(&self, f: F) {
let strings = &self.interp.pat.strings;
self.interp.frames.borrow().iter().filter_map(|frame| {
match frame.op() {
| PatternElement::Value { name_and_value, .. }
| PatternElement::Tag { name_and_value, .. }
if frame.matches && name_and_value.is_here()
=> {
Some(&*strings[name_and_value.here().unwrap()])
},
_ => None,
}
}).for_each(f)
}
We'd like to have this:
fn for_names(&self) -> impl Iterator<Item=&'pat str> {
let strings = &self.interp.pat.strings;
self.interp.frames.borrow().iter().filter_map(|frame| {
match frame.op() {
| PatternElement::Value { name_and_value, .. }
| PatternElement::Tag { name_and_value, .. }
if frame.matches && name_and_value.is_here()
=> {
Some(&*strings[name_and_value.here().unwrap()])
},
_ => None,
}
})
}
But because the iter()
comes from a Ref
some lifetime issues happen.
Unfortunately we really have no idea how to fix this. self_cell
doesn't work here because it doesn't support lifetimes (specifically the 'pat
) so that's unfortunate.
vague
October 8, 2022, 1:22am
2
Have you considered the lifetime elision ?
fn for_names(&self) -> impl Iterator<Item = &str> {
fn for_names2(&self, f: impl FnMut(&str)) {
Soni
October 8, 2022, 1:27am
3
Ah right uh. self.interp.frames
is a &'state RefCell<Vec<Frame<...>>>
. We guess that wasn't as clear as we were hoping.
so no, that doesn't work here.
see, std has Ref-to-Ref projection (&T -> &U, aka Ref::map) but not Ref-to-"BorrowsRef" (&T -> U<'_>) projection. and those would only be possible with GATs anyway.
Soni
October 8, 2022, 9:47am
4
We could do something along the lines of
struct RefIteratorInner<'this, 'a, T> {
borrowed: Ref<'a, Vec<T>>,
owned: RefCell<Option<Iter<'this, T>>>, // need the option because reasons
}
struct RefIterator<'a, T> {
inner: Pin<Box<Holder<'a, RefIteratorKey<'a, T>>>>,
}
impl<'a, T> Iterator for RefIterator<'a, T> {
fn next(&mut self) {
self.inner.as_ref().operate_in(|inner|{ // can't actually use a closure here but pretend we could
inner.owned.borrow_mut().unwrap().next()
})
}
}
if we were to use selfref
. We believe this to be less than ideal tho.
Have you considered using a more powerful self-referential library, like ouroboros?
Soni
October 8, 2022, 10:07am
6
ouroboros is actually less powerful because it can't do this at all.
use std::cell::{Ref, RefCell};
use std::slice::Iter;
pub type Frame = std::ops::Range<usize>;
pub struct MyStruct {
some_string: String,
frames: RefCell<Vec<Frame>>,
}
impl MyStruct {
pub fn for_names(&self) -> MyIterator<'_> {
MyIterator::new(
self.frames.borrow(),
|frames| frames.iter(),
&self.some_string,
)
}
}
#[ouroboros::self_referencing]
pub struct MyIterator<'my_struct> {
frames: Ref<'my_struct, Vec<Frame>>,
#[borrows(frames)]
#[covariant]
iter: Iter<'this, Frame>,
some_string: &'my_struct String,
}
impl<'my_struct> Iterator for MyIterator<'my_struct> {
type Item = &'my_struct str;
fn next(&mut self) -> Option<Self::Item> {
let frame = self.with_iter_mut(|iter| {
iter.find_map(|frame| {
// let's pretend you do some real work here
Some(frame)
})
})?;
let frame = frame.start..frame.end;
Some(&self.borrow_some_string()[frame])
}
}
This seems to work and match your example though
1 Like
Soni
October 8, 2022, 10:44am
8
wait what since when does ouroboros work with lifetimes, we thought it didn't?
My example seems to works since ouroboros 0.8, when they introduced #[covariant]
(the latest version is 0.15)
Soni
October 9, 2022, 11:04am
10
hmm, we wonder if there's a crate that works with Ref
directly. like uh.
because like, Ref
doesn't actually require pinning or allocation to borrow like this. but using any of the selfref crates available (selfref, ouroboros, self_cell, etc) would require pinning/allocations because they need those for soundness.
then again, since this isn't a public API, maybe we shouldn't bother. it's not like it makes much of a difference to return the iterator vs to call a closure with it for our needs.
Soni
October 10, 2022, 11:32pm
11
oh, we guess we can use a level of indirection actually. have a wrapper type which contains the Ref
or RefMut
, and then have methods to iterate on it.
system
Closed
January 8, 2023, 11:32pm
12
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.