It works for simple examples like the for
loop. But it fails in many other cases, such as ctx.list(...).next().unwrap()
, or ctx.list(...).map(...)
. One way to fix this would be to allow access to the data in a lifetime-restricted callback, then iterate over its outputs (i.e., implicitly map
). This way, we're working with the lifetime system instead of fighting against it. As a proof of concept, it compiles just fine, and allows us to get rid of the id
and valid
fields:
diff --git a/src/lib.rs b/src/lib.rs
index cf04809..29240c5 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -379,8 +379,6 @@ mod valid_context {
// available through get_info.
list: ptr::null_mut(),
_t: &CONTEXTS,
- id: 0,
- valid: Default::default(),
},
_hidden: (),
}
@@ -1569,13 +1567,14 @@ impl<'a, 'ph: 'a> ValidContext<'a, 'ph> {
///
/// fn print_contexts(ph: &mut PluginHandle<'_>) {
/// ph.ensure_valid_context(|ph| {
- /// for context in ph.list(&Contexts) {
- /// write!(ph, "{}", context.name().unwrap());
+ /// for name in ph.list(&Contexts, |context| context.name()) {
+ /// write!(ph, "{}", name.unwrap());
/// }
/// })
/// }
/// ```
- pub fn list<T: list::List>(&'a self, t: &'a T) -> list::Entries<'a, 'ph, T> {
+ pub fn list<'b, T: list::List, U, F>(&'b self, t: &'b T, f: F) -> list::Entries<'b, 'ph, T, F>
+ where F: FnMut(list::Fields<'_, 'ph, T>) -> U {
let ph = &self.ph;
let list = CString::new(&*t.name()).unwrap();
let list = unsafe {
@@ -1585,7 +1584,7 @@ impl<'a, 'ph: 'a> ValidContext<'a, 'ph> {
context: self.ph,
list: list,
t: t,
- valid: Default::default(),
+ f: f,
}
}
diff --git a/src/list.rs b/src/list.rs
index 5d40e5a..ff320ae 100644
--- a/src/list.rs
+++ b/src/list.rs
@@ -17,9 +17,7 @@
//! List support module.
use std::borrow::Cow;
-use std::cell::Cell;
use std::ffi::CStr;
-use std::rc::Rc;
use crate::Context;
use crate::wrap_context;
@@ -52,11 +50,11 @@ pub struct Notify;
pub struct Users;
/// Entries.
-pub struct Entries<'a, 'ph, T> {
+pub struct Entries<'a, 'ph, T, F> {
pub(crate) context: &'a crate::PluginHandle<'ph>,
pub(crate) list: *mut crate::internals::HexchatList,
pub(crate) t: &'a T,
- pub(crate) valid: Option<Rc<Cell<usize>>>,
+ pub(crate) f: F,
}
/// Fields.
@@ -64,8 +62,6 @@ pub struct Fields<'a, 'ph, T> {
pub(crate) context: &'a crate::PluginHandle<'ph>,
pub(crate) list: *mut crate::internals::HexchatList,
pub(crate) _t: &'a T,
- pub(crate) id: usize,
- pub(crate) valid: Rc<Cell<usize>>,
}
// impls
@@ -96,47 +92,31 @@ impl List for Users {
fn name(&self) -> Cow<'static, str> { "users".into() }
}
-impl<'a, 'ph, T> Drop for Entries<'a, 'ph, T> {
+impl<'a, 'ph, T, F> Drop for Entries<'a, 'ph, T, F> {
fn drop(&mut self) {
let ph = &self.context;
- if let Some(ref valid) = self.valid {
- valid.set(usize::MAX);
- }
unsafe {
ph_call!(hexchat_list_free(ph, self.list));
}
}
}
-impl<'a, 'ph, T> Iterator for Entries<'a, 'ph, T> {
- type Item = Fields<'a, 'ph, T>;
+impl<'a, 'ph, T, U, F> Iterator for Entries<'a, 'ph, T, F>
+where F: FnMut(Fields<'_, 'ph, T>) -> U {
+ type Item = U;
fn next(&mut self) -> Option<Self::Item> {
let ph = &self.context;
if unsafe {
ph_call!(hexchat_list_next(ph, self.list))
} != 0 {
- let valid = if let Some(ref valid) = self.valid {
- debug_assert!(valid.get() < usize::MAX);
- valid.set(valid.get() + 1);
- Rc::clone(valid)
- } else {
- let valid: Rc<Cell<usize>> = Default::default();
- self.valid = Some(Rc::clone(&valid));
- valid
- };
- let id = valid.get();
- Some(Fields {
+ let fields = Fields {
context: self.context,
list: self.list,
_t: self.t,
- valid: valid,
- id: id,
- })
+ };
+ Some((self.f)(fields))
} else {
- if let Some(ref valid) = self.valid {
- valid.set(usize::MAX);
- }
None
}
}
@@ -145,10 +125,6 @@ impl<'a, 'ph, T> Iterator for Entries<'a, 'ph, T> {
macro_rules! field {
($(#[$m:meta])* $f:ident, unsafe {$n:expr}, $r:ty, $ph:ident, $c:ident, $($conv:tt)+) => {
$(#[$m])* pub fn $f(&self) -> $r {
- assert_eq!(
- self.id, self.valid.get(),
- "instances of Fields are invalidated by next()",
- );
let $ph = &self.context;
const NAME: &'static CStr = unsafe {
CStr::from_bytes_with_nul_unchecked(