Repository: mintlayer/mintlayer-core
Pull request #706 (2023-02-15 17:44:57)

storage: Use GATs to simplify backend traits
These traits have been introduced as a workaround to the lack of higher-ranked types in Rust. Now a limited version of higher-ranked types is available in form of Generic Associated Types so the workaround is no longer necessary.


Patch:
diff --git a/storage/core/src/adaptor/locking/mod.rs b/storage/core/src/adaptor/locking/mod.rs
index 1497e3a6a..55019701d 100644
--- a/storage/core/src/adaptor/locking/mod.rs
+++ b/storage/core/src/adaptor/locking/mod.rs
@@ -20,7 +20,7 @@ mod prefix_iter_rw;
 
 use crate::{
     adaptor::{Construct, CoreOps},
-    backend::{self, PrefixIter, ReadOps, WriteOps},
+    backend::{self, ReadOps, WriteOps},
     Data, DbDesc, DbMapCount, DbMapId, DbMapsData,
 };
 
@@ -31,19 +31,13 @@ use utils::{const_value::ConstValue, sync};
 pub struct TxRo<'tx, T>(sync::RwLockReadGuard<'tx, T>);
 
 impl<'tx, T: ReadOps> ReadOps for TxRo<'tx, T> {
+    type PrefixIter<'i> = T::PrefixIter<'i> where Self: 'i;
+
     fn get(&self, map_id: DbMapId, key: &[u8]) -> crate::Result<Option<Cow<[u8]>>> {
         self.0.get(map_id, key)
     }
-}
-
-impl<'tx, 'i, T: PrefixIter<'i>> PrefixIter<'i> for TxRo<'tx, T> {
-    type Iterator = T::Iterator;
 
-    fn prefix_iter<'m: 'i>(
-        &'m self,
-        map_id: DbMapId,
-        prefix: Data,
-    ) -> crate::Result<Self::Iterator> {
+    fn prefix_iter(&self, map_id: DbMapId, prefix: Data) -> crate::Result<Self::PrefixIter<'_>> {
         self.0.prefix_iter(map_id, prefix)
     }
 }
@@ -67,22 +61,16 @@ impl<'tx, T> TxRw<'tx, T> {
 }
 
 impl<'tx, T: ReadOps> ReadOps for TxRw<'tx, T> {
+    type PrefixIter<'i> = prefix_iter_rw::Iter<'i, T> where Self: 'i;
+
     fn get(&self, map_id: DbMapId, key: &[u8]) -> crate::Result<Option<Cow<[u8]>>> {
         self.deltas[map_id].get(key).map_or_else(
             || self.db.get(map_id, key),
             |x| Ok(x.as_deref().map(|p| p.into())),
         )
     }
-}
 
-impl<'tx, 'i, T: PrefixIter<'i>> PrefixIter<'i> for TxRw<'tx, T> {
-    type Iterator = prefix_iter_rw::Iter<'i, T>;
-
-    fn prefix_iter<'m: 'i>(
-        &'m self,
-        map_id: DbMapId,
-        prefix: Data,
-    ) -> crate::Result<Self::Iterator> {
+    fn prefix_iter(&self, map_id: DbMapId, prefix: Data) -> crate::Result<Self::PrefixIter<'_>> {
         prefix_iter_rw::iter(self, map_id, prefix)
     }
 }
@@ -134,18 +122,16 @@ impl<T> utils::shallow_clone::ShallowClone for TransactionLockImpl<T> {
     }
 }
 
-impl<'tx, T: 'tx + ReadOps> backend::TransactionalRo<'tx> for TransactionLockImpl<T> {
-    type TxRo = TxRo<'tx, T>;
+impl<T: CoreOps + Sync + Send + 'static> backend::BackendImpl for TransactionLockImpl<T> {
+    type TxRo<'a> = TxRo<'a, T>;
+
+    type TxRw<'a> = TxRw<'a, T>;
 
-    fn transaction_ro<'st: 'tx>(&'st self) -> crate::Result<Self::TxRo> {
+    fn transaction_ro(&self) -> crate::Result<Self::TxRo<'_>> {
         Ok(TxRo(self.db.read().expect("lock to be alive")))
     }
-}
 
-impl<'tx, T: 'tx + ReadOps + WriteOps> backend::TransactionalRw<'tx> for TransactionLockImpl<T> {
-    type TxRw = TxRw<'tx, T>;
-
-    fn transaction_rw<'st: 'tx>(&'st self, _: Option<usize>) -> crate::Result<Self::TxRw> {
+    fn transaction_rw(&self, _size: Option<usize>) -> crate::Result<Self::TxRw<'_>> {
         Ok(TxRw {
             db: self.db.write().expect("lock to be alive"),
             deltas: DbMapsData::new(*self.num_maps, |_| BTreeMap::new()),
@@ -153,8 +139,6 @@ impl<'tx, T: 'tx + ReadOps + WriteOps> backend::TransactionalRw<'tx> for Transac
     }
 }
 
-impl<T: CoreOps + Sync + Send + 'static> backend::BackendImpl for TransactionLockImpl<T> {}
-
 /// Add lock-based transactions to given bare backend implementation.
 ///
 /// Given a type `T` implementing core database operations [CoreOps], this creates a full-featured
diff --git a/storage/core/src/adaptor/locking/prefix_iter_rw.rs b/storage/core/src/adaptor/locking/prefix_iter_rw.rs
index 04f0a5d41..8b1118761 100644
--- a/storage/core/src/adaptor/locking/prefix_iter_rw.rs
+++ b/storage/core/src/adaptor/locking/prefix_iter_rw.rs
@@ -15,7 +15,9 @@
 
 //! Internal functions and types used in the implementation of prefix iterator for RW transactions
 
-use super::{Data, DbMapId, PrefixIter, TxRw};
+use crate::backend::ReadOps;
+
+use super::{Data, DbMapId, TxRw};
 use itertools::EitherOrBoth;
 
 // The prefix iterator type for mutable transaction is a fairly complicated type. Here, we
@@ -24,7 +26,7 @@ type DataPair = (Data, Data);
 type DataPairRef<'a> = (&'a [u8], &'a Option<Data>);
 type KeyCompareFn = fn(&DataPair, &DataPairRef<'_>) -> std::cmp::Ordering;
 type ItemMergeFn = fn(itertools::EitherOrBoth<DataPair, DataPairRef<'_>>) -> Option<DataPair>;
-type DbIter<'i, T> = <T as PrefixIter<'i>>::Iterator;
+type DbIter<'i, T> = <T as ReadOps>::PrefixIter<'i>;
 type DeltaIter<'i> = crate::util::PrefixIter<'i, Option<Data>>;
 type JoinIter<'i, T> = itertools::MergeJoinBy<DbIter<'i, T>, DeltaIter<'i>, KeyCompareFn>;
 pub type Iter<'i, T> = std::iter::FilterMap<JoinIter<'i, T>, ItemMergeFn>;
@@ -48,7 +50,7 @@ fn merger(item: EitherOrBoth<DataPair, DataPairRef<'_>>) -> Option<(Data, Data)>
 }
 
 /// Create the iterator
-pub fn iter<'tx, 'i, 'm: 'i, T: PrefixIter<'i>>(
+pub fn iter<'tx, 'i, 'm: 'i, T: ReadOps>(
     tx: &'m TxRw<'tx, T>,
     map_id: DbMapId,
     prefix: Data,
diff --git a/storage/core/src/backend.rs b/storage/core/src/backend.rs
index 4cefe4531..64082a4de 100644
--- a/storage/core/src/backend.rs
+++ b/storage/core/src/backend.rs
@@ -21,23 +21,18 @@ use utils::shallow_clone::ShallowClone;
 
 pub use crate::{Data, DbDesc, DbMapId};
 
-/// Types providing capability of iterating over keys with given prefix
-pub trait PrefixIter<'i> {
-    /// The iterator type
-    type Iterator: 'i + Iterator<Item = (Data, Data)>;
-
-    /// Get iterator over key-value pairs where the key has given prefix
-    fn prefix_iter<'m: 'i>(
-        &'m self,
-        map_id: DbMapId,
-        prefix: Data,
-    ) -> crate::Result<Self::Iterator>;
-}
-
 /// Read-only database operations
-pub trait ReadOps: for<'i> PrefixIter<'i> {
+pub trait ReadOps {
+    /// The prefix iterator type
+    type PrefixIter<'i>: Iterator<Item = (Data, Data)> + 'i
+    where
+        Self: 'i;
+
     /// Get value associated with given key.
     fn get(&self, map_id: DbMapId, key: &[u8]) -> crate::Result<Option<Cow<[u8]>>>;
+
+    /// Get iterator over key-value pairs where the key has given prefix
+    fn prefix_iter(&self, map_id: DbMapId, prefix: Data) -> crate::Result<Self::PrefixIter<'_>>;
 }
 
 /// Write database operation
@@ -62,32 +57,19 @@ pub trait TxRw: ReadOps + WriteOps {
     fn commit(self) -> crate::Result<()>;
 }
 
-/// Read-only transactional interface to the storage
-pub trait TransactionalRo<'tx> {
+/// Storage backend internal implementation type
+pub trait BackendImpl: 'static + Send + Sync + ShallowClone {
     /// Read-only transaction internal type
-    type TxRo: TxRo + 'tx;
+    type TxRo<'a>: TxRo + 'a;
 
-    /// Start a read-only transaction
-    fn transaction_ro<'st: 'tx>(&'st self) -> crate::Result<Self::TxRo>;
-}
-
-/// Read-write transactional interface to the storage
-pub trait TransactionalRw<'tx> {
     /// Start a read-write transaction
-    type TxRw: TxRw + 'tx;
+    type TxRw<'a>: TxRw + 'a;
 
-    /// Start a read-write transaction
-    fn transaction_rw<'st: 'tx>(&'st self, size: Option<usize>) -> crate::Result<Self::TxRw>;
-}
+    /// Start a read-only transaction
+    fn transaction_ro(&self) -> crate::Result<Self::TxRo<'_>>;
 
-/// Storage backend internal implementation type
-pub trait BackendImpl
-where
-    Self: 'static + Send + Sync,
-    Self: for<'tx> TransactionalRo<'tx>,
-    Self: for<'tx> TransactionalRw<'tx>,
-    Self: ShallowClone,
-{
+    /// Start a read-write transaction
+    fn transaction_rw(&self, size: Option<usize>) -> crate::Result<Self::TxRw<'_>>;
 }
 
 /// Storage backend type. Used to set up storage.
diff --git a/storage/inmemory/src/lib.rs b/storage/inmemory/src/lib.rs
index 547f6b5c0..265923db6 100644
--- a/storage/inmemory/src/lib.rs
+++ b/storage/inmemory/src/lib.rs
@@ -32,19 +32,17 @@ impl<'i> Iterator for PrefixIter<'i> {
 pub struct StorageMaps(DbMapsData<Map>);
 
 impl backend::ReadOps for StorageMaps {
+    type PrefixIter<'i> = PrefixIter<'i>;
+
     fn get(&self, map_id: DbMapId, key: &[u8]) -> storage_core::Result<Option<Cow<[u8]>>> {
         Ok(self.0[map_id].get(key).map(|p| p.into()))
     }
-}
-
-impl<'i> backend::PrefixIter<'i> for StorageMaps {
-    type Iterator = PrefixIter<'i>;
 
-    fn prefix_iter<'m: 'i>(
-        &'m self,
+    fn prefix_iter(
+        &self,
         map_id: DbMapId,
         prefix: Data,
-    ) -> storage_core::Result<Self::Iterator> {
+    ) -> storage_core::Result<Self::PrefixIter<'_>> {
         Ok(PrefixIter(util::PrefixIter::new(&self.0[map_id], prefix)))
     }
 }
diff --git a/storage/lmdb/src/lib.rs b/storage/lmdb/src/lib.rs
index 2326b5848..b9ba24546 100644
--- a/storage/lmdb/src/lib.rs
+++ b/storage/lmdb/src/lib.rs
@@ -24,10 +24,7 @@ use std::{borrow::Cow, path::PathBuf};
 use initial_map_size::InitialMapSize;
 use lmdb::Cursor;
 use resize_callback::MapResizeCallback;
-use storage_core::{
-    backend::{self, TransactionalRo, TransactionalRw},
-    Data, DbDesc, DbMapDesc, DbMapId, DbMapsData,
-};
+use storage_core::{backend, Data, DbDesc, DbMapDesc, DbMapId, DbMapsData};
 use utils::const_value::ConstValue;
 use utils::sync::Arc;
 
@@ -66,14 +63,20 @@ pub struct DbTx<'m, Tx> {
 type DbTxRo<'a> = DbTx<'a, lmdb::RoTransaction<'a>>;
 type DbTxRw<'a> = DbTx<'a, lmdb::RwTransaction<'a>>;
 
-impl<'s, 'i, Tx: lmdb::Transaction> backend::PrefixIter<'i> for DbTx<'s, Tx> {
-    type Iterator = PrefixIter<'i, lmdb::RoCursor<'i>>;
+impl<Tx: lmdb::Transaction> backend::ReadOps for DbTx<'_, Tx> {
+    type PrefixIter<'i> = PrefixIter<'i, lmdb::RoCursor<'i>> where Self: 'i;
 
-    fn prefix_iter<'t: 'i>(
-        &'t self,
+    fn get(&self, map_id: DbMapId, key: &[u8]) -> storage_core::Result<Option<Cow<[u8]>>> {
+        self.tx
+            .get(self.backend.dbs[map_id], &key)
+            .map_or_else(error::process_with_none, |x| Ok(Some(x.into())))
+    }
+
+    fn prefix_iter(
+        &self,
         map_id: DbMapId,
         prefix: Data,
-    ) -> storage_core::Result<Self::Iterator> {
+    ) -> storage_core::Result<Self::PrefixIter<'_>> {
         let cursor = self
             .tx
             .open_ro_cursor(self.backend.dbs[map_id])
@@ -87,14 +90,6 @@ impl<'s, 'i, Tx: lmdb::Transaction> backend::PrefixIter<'i> for DbTx<'s, Tx> {
     }
 }
 
-impl<Tx: lmdb::Transaction> backend::ReadOps for DbTx<'_, Tx> {
-    fn get(&self, map_id: DbMapId, key: &[u8]) -> storage_core::Result<Option<Cow<[u8]>>> {
-        self.tx
-            .get(self.backend.dbs[map_id], &key)
-            .map_or_else(error::process_with_none, |x| Ok(Some(x.into())))
-    }
-}
-
 impl backend::WriteOps for DbTx<'_, lmdb::RwTransaction<'_>> {
     fn put(&mut self, map_id: DbMapId, key: Data, val: Data) -> storage_core::Result<()> {
         let db = self.backend.dbs[map_id];
@@ -187,26 +182,6 @@ impl LmdbImpl {
     }
 }
 
-impl<'tx> TransactionalRo<'tx> for LmdbImpl {
-    type TxRo = DbTxRo<'tx>;
-
-    fn transaction_ro<'st: 'tx>(&'st self) -> storage_core::Result<Self::TxRo> {
-        self.start_transaction(lmdb::Environment::begin_ro_txn)
-    }
-}
-
-impl<'tx> TransactionalRw<'tx> for LmdbImpl {
-    type TxRw = DbTxRw<'tx>;
-
-    fn transaction_rw<'st: 'tx>(
-        &'st self,
-        size: Option<usize>,
-    ) -> storage_core::Result<Self::TxRw> {
-        self.resize_if_resize_scheduled();
-        self.start_transaction(|env| lmdb::Environment::begin_rw_txn(env, size))
-    }
-}
-
 impl utils::shallow_clone::ShallowClone for LmdbImpl {
     fn shallow_clone(&self) -> Self {
         Self {
@@ -216,7 +191,20 @@ impl utils::shallow_clone::ShallowClone for LmdbImpl {
         }
     }
 }
-impl backend::BackendImpl for LmdbImpl {}
+impl backend::BackendImpl for LmdbImpl {
+    type TxRo<'a> = DbTxRo<'a>;
+
+    type TxRw<'a> = DbTxRw<'a>;
+
+    fn transaction_ro(&self) -> storage_core::Result<Self::TxRo<'_>> {
+        self.start_transaction(lmdb::Environment::begin_ro_txn)
+    }
+
+    fn transaction_rw(&self, size: Option<usize>) -> storage_core::Result<Self::TxRw<'_>> {
+        self.resize_if_resize_scheduled();
+        self.start_transaction(|env| lmdb::Environment::begin_rw_txn(env, size))
+    }
+}
 
 pub struct Lmdb {
     path: PathBuf,
diff --git a/storage/sqlite/src/lib.rs b/storage/sqlite/src/lib.rs
index bfa19da7a..4d687b09e 100644
--- a/storage/sqlite/src/lib.rs
+++ b/storage/sqlite/src/lib.rs
@@ -26,10 +26,7 @@ use std::sync::{Mutex, MutexGuard};
 
 use crate::queries::SqliteQueries;
 use error::process_sqlite_error;
-use storage_core::{
-    backend::{self, TransactionalRo, TransactionalRw},
-    Data, DbDesc, DbMapId,
-};
+use storage_core::{backend, Data, DbDesc, DbMapId};
 use utils::shallow_clone::ShallowClone;
 use utils::sync::Arc;
 
@@ -100,14 +97,29 @@ impl Drop for DbTx<'_> {
     }
 }
 
-impl<'s, 'i> backend::PrefixIter<'i> for DbTx<'s> {
-    type Iterator = PrefixIter;
+impl backend::ReadOps for DbTx<'_> {
+    type PrefixIter<'i> = PrefixIter where Self: 'i;
+
+    fn get(&self, map_id: DbMapId, key: &[u8]) -> storage_core::Result<Option<Cow<[u8]>>> {
+        let mut stmt = self
+            .connection
+            .prepare_cached(self.queries[map_id].get_query.as_str())
+            .map_err(process_sqlite_error)?;
 
-    fn prefix_iter<'t: 'i>(
-        &'t self,
+        let params = (key,);
+        let res = stmt
+            .query_row(params, |row| row.get::<usize, Vec<u8>>(0))
+            .optional()
+            .map_err(process_sqlite_error)?;
+        let res = res.map(|v| v.into());
+        Ok(res)
+    }
+
+    fn prefix_iter(
+        &self,
         map_id: DbMapId,
         prefix: Data,
-    ) -> storage_core::Result<Self::Iterator> {
+    ) -> storage_core::Result<Self::PrefixIter<'_>> {
         // TODO check if prefix.is_empty()
         // TODO Perform the filtering in the SQL query itself
         let mut stmt = self
@@ -132,23 +144,6 @@ impl<'s, 'i> backend::PrefixIter<'i> for DbTx<'s> {
     }
 }
 
-impl backend::ReadOps for DbTx<'_> {
-    fn get(&self, map_id: DbMapId, key: &[u8]) -> storage_core::Result<Option<Cow<[u8]>>> {
-        let mut stmt = self
-            .connection
-            .prepare_cached(self.queries[map_id].get_query.as_str())
-            .map_err(process_sqlite_error)?;
-
-        let params = (key,);
-        let res = stmt
-            .query_row(params, |row| row.get::<usize, Vec<u8>>(0))
-            .optional()
-            .map_err(process_sqlite_error)?;
-        let res = res.map(|v| v.into());
-        Ok(res)
-    }
-}
-
 impl backend::WriteOps for DbTx<'_> {
     fn put(&mut self, map_id: DbMapId, key: Data, val: Data) -> storage_core::Result<()> {
         let mut stmt = self
@@ -202,33 +197,26 @@ impl SqliteImpl {
     }
 }
 
-impl<'tx> TransactionalRo<'tx> for SqliteImpl {
-    type TxRo = DbTx<'tx>;
-
-    fn transaction_ro<'st: 'tx>(&'st self) -> storage_core::Result<Self::TxRo> {
-        self.start_transaction()
+impl ShallowClone for SqliteImpl {
+    fn shallow_clone(&self) -> Self {
+        Self(self.0.shallow_clone())
     }
 }
 
-impl<'tx> TransactionalRw<'tx> for SqliteImpl {
-    type TxRw = DbTx<'tx>;
+impl backend::BackendImpl for SqliteImpl {
+    type TxRo<'a> = DbTx<'a>;
+
+    type TxRw<'a> = DbTx<'a>;
 
-    fn transaction_rw<'st: 'tx>(
-        &'st self,
-        _size: Option<usize>,
-    ) -> storage_core::Result<Self::TxRw> {
+    fn transaction_ro(&self) -> storage_core::Result<Self::TxRo<'_>> {
         self.start_transaction()
     }
-}
 
-impl ShallowClone for SqliteImpl {
-    fn shallow_clone(&self) -> Self {
-        Self(self.0.shallow_clone())
+    fn transaction_rw(&self, _size: Option<usize>) -> storage_core::Result<Self::TxRw<'_>> {
+        self.start_transaction()
     }
 }
 
-impl backend::BackendImpl for SqliteImpl {}
-
 #[derive(Eq, PartialEq, Clone, Debug)]
 enum SqliteStorageMode {
     InMemory,
diff --git a/storage/src/database/internal.rs b/storage/src/database/internal.rs
index 84282b117..52989a11d 100644
--- a/storage/src/database/internal.rs
+++ b/storage/src/database/internal.rs
@@ -20,7 +20,7 @@ use std::borrow::Cow;
 use crate::schema;
 use serialization::{encoded::Encoded, EncodeLike};
 use storage_core::{
-    backend::{self, PrefixIter, ReadOps},
+    backend::{self, ReadOps},
     Backend, DbMapId,
 };
 
@@ -31,11 +31,11 @@ pub trait TxImpl {
 }
 
 impl<'tx, B: Backend, Sch> TxImpl for super::TransactionRo<'tx, B, Sch> {
-    type Impl = <B::Impl as backend::TransactionalRo<'tx>>::TxRo;
+    type Impl = <B::Impl as backend::BackendImpl>::TxRo<'tx>;
 }
 
 impl<'tx, B: Backend, Sch> TxImpl for super::TransactionRw<'tx, B, Sch> {
-    type Impl = <B::Impl as backend::TransactionalRw<'tx>>::TxRw;
+    type Impl = <B::Impl as backend::BackendImpl>::TxRw<'tx>;
 }
 
 /// Get a value from the database backend as a SCALE-encoded object
@@ -59,11 +59,11 @@ impl<DbMap: schema::DbMap, I: Iterator<Item = (DbMap::Key, Encoded<Vec<u8>, DbMa
 {
 }
 
-pub fn prefix_iter<'tx, DbMap: schema::DbMap, Tx: PrefixIter<'tx>>(
-    dbtx: &'tx Tx,
+pub fn prefix_iter<DbMap: schema::DbMap, Tx: ReadOps>(
+    dbtx: &Tx,
     map_id: DbMapId,
     prefix: Vec<u8>,
-) -> crate::Result<impl 'tx + EntryIterator<DbMap>> {
+) -> crate::Result<impl '_ + EntryIterator<DbMap>> {
     dbtx.prefix_iter(map_id, prefix).map(|iter| {
         iter.map(|(k, v)| {
             (
diff --git a/storage/src/database/mod.rs b/storage/src/database/mod.rs
index c36eec459..42a08b4bf 100644
--- a/storage/src/database/mod.rs
+++ b/storage/src/database/mod.rs
@@ -71,18 +71,15 @@ impl<B: Backend, Sch: Schema> Storage<B, Sch> {
     }
 
     /// Start a read-only transaction
-    pub fn transaction_ro<'tx, 'st: 'tx>(&'st self) -> crate::Result<TransactionRo<'tx, B, Sch>> {
-        let dbtx = backend::TransactionalRo::transaction_ro(&self.backend)?;
+    pub fn transaction_ro(&self) -> crate::Result<TransactionRo<'_, B, Sch>> {
+        let dbtx = backend::BackendImpl::transaction_ro(&self.backend)?;
         let _schema = std::marker::PhantomData;
         Ok(TransactionRo { dbtx, _schema })
     }
 
     /// Start a read-write transaction
-    pub fn transaction_rw<'tx, 'st: 'tx>(
-        &'st self,
-        size: Option<usize>,
-    ) -> crate::Result<TransactionRw<'tx, B, Sch>> {
-        let dbtx = backend::TransactionalRw::transaction_rw(&self.backend, size)?;
+    pub fn transaction_rw(&self, size: Option<usize>) -> crate::Result<TransactionRw<'_, B, Sch>> {
+        let dbtx = backend::BackendImpl::transaction_rw(&self.backend, size)?;
         let _schema = std::marker::PhantomData;
         Ok(TransactionRw { dbtx, _schema })
     }
diff --git a/storage/src/database/raw.rs b/storage/src/database/raw.rs
index babe6b01b..4bf36148e 100644
--- a/storage/src/database/raw.rs
+++ b/storage/src/database/raw.rs
@@ -20,7 +20,7 @@ use crate::{
     Backend, Storage,
 };
 use std::collections::BTreeMap;
-use storage_core::backend::PrefixIter;
+use storage_core::backend::ReadOps;
 
 pub use storage_core::Data;
 


Test patch:
diff --git a/storage/backend-test-suite/src/model.rs b/storage/backend-test-suite/src/model.rs
index c234935ce..b60756cea 100644
--- a/storage/backend-test-suite/src/model.rs
+++ b/storage/backend-test-suite/src/model.rs
@@ -79,8 +79,8 @@ impl Model {
     }
 
     /// New model obtained by dumping a database in a transaction. May contain uncommitted changes.
-    pub fn from_tx<Tx: for<'tx> backend::PrefixIter<'tx>>(tx: &Tx, map_id: DbMapId) -> Self {
-        Model(backend::PrefixIter::prefix_iter(tx, map_id, Data::new()).unwrap().collect())
+    pub fn from_tx<Tx: backend::ReadOps>(tx: &Tx, map_id: DbMapId) -> Self {
+        Model(backend::ReadOps::prefix_iter(tx, map_id, Data::new()).unwrap().collect())
     }
 
     /// Get the inner map
diff --git a/storage/backend-test-suite/src/prelude.rs b/storage/backend-test-suite/src/prelude.rs
index 8fbd06346..8bed68b05 100644
--- a/storage/backend-test-suite/src/prelude.rs
+++ b/storage/backend-test-suite/src/prelude.rs
@@ -16,9 +16,7 @@
 // Re-export a bunch of often used items
 pub use crate::model::{ApplyActions, Model, WriteAction};
 pub use storage_core::{
-    backend::{
-        Backend, Data, PrefixIter, ReadOps, TransactionalRo, TransactionalRw, TxRo, TxRw, WriteOps,
-    },
+    backend::{Backend, BackendImpl, Data, ReadOps, TxRo, TxRw, WriteOps},
     DbDesc, DbMapCount, DbMapDesc, DbMapId, DbMapsData,
 };
 pub use utils::{sync, thread};
diff --git a/storage/lmdb/src/resize_tests.rs b/storage/lmdb/src/resize_tests.rs
index 55346c82b..4d3ad0169 100644
--- a/storage/lmdb/src/resize_tests.rs
+++ b/storage/lmdb/src/resize_tests.rs
@@ -17,10 +17,7 @@ use std::{collections::BTreeMap, sync::Mutex};
 
 use memsize::MemSize;
 use rstest::rstest;
-use storage_core::{
-    backend::{ReadOps, TxRw, WriteOps},
-    Backend,
-};
+use storage_core::backend::{Backend, BackendImpl, ReadOps, TxRw, WriteOps};
 use tempdir::TempDir;
 use test_utils::random::make_seedable_rng;
 use test_utils::random::{CryptoRng, Rng, Seed};