I'm mapping each element too (wrapping it in this example). But in my later code, the wrap
function will perform other tasks too. This is my real-life code, but I wanted to keep the example simple:
unsafe fn open_or_opt_create_dbs<'a, K, V, C>(
self: &Arc<Self>,
options: impl IntoIterator<
IntoIter = impl Iterator<Item = &'a DbOptions<K, V, C, Option<CString>>> + ExactSizeIterator,
>,
create: bool,
) -> Result<Vec<Db<K, V, C>>, io::Error>
where
K: ?Sized + Storable,
V: ?Sized + Storable,
C: Constraint,
{
let options = options.into_iter();
let mut dbs = Vec::with_capacity(options.len());
let db_guard = self.db_mutex.lock().unwrap();
let mut txn_inner = MaybeUninit::<*mut lmdb::MDB_txn>::uninit();
check_err_code(lmdb::mdb_txn_begin(
self.inner,
null_mut(),
if create {
0 as LmdbFlags
} else {
lmdb::MDB_RDONLY as LmdbFlags
},
txn_inner.as_mut_ptr(),
))?;
let txn_inner = txn_inner.assume_init();
let txn = TxnBackend {
env_backend: self,
inner: txn_inner,
cursors: Default::default(),
};
for option in options {
let mut db_inner = MaybeUninit::<lmdb::MDB_dbi>::uninit();
let mut flags = option.cleansed_lmdb_flags();
if create {
flags |= lmdb::MDB_CREATE as LmdbFlags;
}
check_err_code(lmdb::mdb_dbi_open(
txn.inner,
option.name.as_ref().map_or(null(), |x| x.as_ptr()),
flags,
db_inner.as_mut_ptr(),
))?;
let db_inner = db_inner.assume_init();
let db_backend = DbBackend {
env_backend: Arc::downgrade(self),
inner: db_inner,
};
if !K::TRIVIAL_CMP && !K::OPTIMIZE_INT {
check_err_code(lmdb::mdb_set_compare(
txn.inner,
db_backend.inner,
Some(compare_function::<K>),
))?;
}
if !V::TRIVIAL_CMP && !V::OPTIMIZE_INT && C::DUPLICATE_KEYS {
check_err_code(lmdb::mdb_set_dupsort(
txn.inner,
db_backend.inner,
Some(compare_function::<V>),
))?;
}
dbs.push(Db {
key: PhantomData,
value: PhantomData,
constraint: PhantomData,
backend: ArcByAddr::new(db_backend),
});
}
txn.commit()?;
drop(db_guard);
Ok(dbs)
}
P.S.: I wanted to design the interface such that the caller can't hold the internal db_mutex
lock for a long time.