From 24e016a57d32f00693932ad46ac04e4f153b159c Mon Sep 17 00:00:00 2001 From: wqfish Date: Tue, 18 Feb 2020 23:59:06 -0800 Subject: [PATCH] Fix lifetime parameter for iterators (#391) --- Cargo.toml | 4 ++- src/db.rs | 44 ++++++++++++++++----------- tests/fail/iterator_outlive_db.rs | 8 +++++ tests/fail/iterator_outlive_db.stderr | 10 ++++++ tests/test_iterator.rs | 8 ++++- 5 files changed, 54 insertions(+), 20 deletions(-) create mode 100644 tests/fail/iterator_outlive_db.rs create mode 100644 tests/fail/iterator_outlive_db.stderr diff --git a/Cargo.toml b/Cargo.toml index 3850ccf..3e7ea5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,9 @@ zstd = ["librocksdb-sys/zstd"] zlib = ["librocksdb-sys/zlib"] bzip2 = ["librocksdb-sys/bzip2"] - [dependencies] libc = "0.2" librocksdb-sys = { path = "librocksdb-sys", version = "6.4.6" } + +[dev-dependencies] +trybuild = "1.0.21" diff --git a/src/db.rs b/src/db.rs index f4a0dd7..fafe067 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1106,12 +1106,12 @@ impl DB { self.cfs.get(name) } - pub fn iterator<'a, 'b: 'a>(&'a self, mode: IteratorMode) -> DBIterator<'b> { + pub fn iterator<'a: 'b, 'b>(&'a self, mode: IteratorMode) -> DBIterator<'b> { let readopts = ReadOptions::default(); self.iterator_opt(mode, &readopts) } - pub fn iterator_opt<'a, 'b: 'a>( + pub fn iterator_opt<'a: 'b, 'b>( &'a self, mode: IteratorMode, readopts: &ReadOptions, @@ -1121,25 +1121,25 @@ impl DB { /// Opens an iterator using the provided ReadOptions. /// This is used when you want to iterate over a specific ColumnFamily with a modified ReadOptions - pub fn iterator_cf_opt( - &self, + pub fn iterator_cf_opt<'a: 'b, 'b>( + &'a self, cf_handle: &ColumnFamily, readopts: &ReadOptions, mode: IteratorMode, - ) -> DBIterator { + ) -> DBIterator<'b> { DBIterator::new_cf(self, cf_handle, &readopts, mode) } /// Opens an iterator with `set_total_order_seek` enabled. /// This must be used to iterate across prefixes when `set_memtable_factory` has been called /// with a Hash-based implementation. - pub fn full_iterator<'a, 'b: 'a>(&'a self, mode: IteratorMode) -> DBIterator<'b> { + pub fn full_iterator<'a: 'b, 'b>(&'a self, mode: IteratorMode) -> DBIterator<'b> { let mut opts = ReadOptions::default(); opts.set_total_order_seek(true); DBIterator::new(self, &opts, mode) } - pub fn prefix_iterator<'a, 'b: 'a, P: AsRef<[u8]>>(&'a self, prefix: P) -> DBIterator<'b> { + pub fn prefix_iterator<'a: 'b, 'b, P: AsRef<[u8]>>(&'a self, prefix: P) -> DBIterator<'b> { let mut opts = ReadOptions::default(); opts.set_prefix_same_as_start(true); DBIterator::new( @@ -1149,22 +1149,30 @@ impl DB { ) } - pub fn iterator_cf(&self, cf_handle: &ColumnFamily, mode: IteratorMode) -> DBIterator { + pub fn iterator_cf<'a: 'b, 'b>( + &'a self, + cf_handle: &ColumnFamily, + mode: IteratorMode, + ) -> DBIterator<'b> { let opts = ReadOptions::default(); DBIterator::new_cf(self, cf_handle, &opts, mode) } - pub fn full_iterator_cf(&self, cf_handle: &ColumnFamily, mode: IteratorMode) -> DBIterator { + pub fn full_iterator_cf<'a: 'b, 'b>( + &'a self, + cf_handle: &ColumnFamily, + mode: IteratorMode, + ) -> DBIterator<'b> { let mut opts = ReadOptions::default(); opts.set_total_order_seek(true); DBIterator::new_cf(self, cf_handle, &opts, mode) } - pub fn prefix_iterator_cf>( - &self, + pub fn prefix_iterator_cf<'a: 'b, 'b, P: AsRef<[u8]>>( + &'a self, cf_handle: &ColumnFamily, prefix: P, - ) -> DBIterator { + ) -> DBIterator<'b> { let mut opts = ReadOptions::default(); opts.set_prefix_same_as_start(true); DBIterator::new_cf( @@ -1176,28 +1184,28 @@ impl DB { } /// Opens a raw iterator over the database, using the default read options - pub fn raw_iterator(&self) -> DBRawIterator { + pub fn raw_iterator<'a: 'b, 'b>(&'a self) -> DBRawIterator<'b> { let opts = ReadOptions::default(); DBRawIterator::new(self, &opts) } /// Opens a raw iterator over the given column family, using the default read options - pub fn raw_iterator_cf(&self, cf_handle: &ColumnFamily) -> DBRawIterator { + pub fn raw_iterator_cf<'a: 'b, 'b>(&'a self, cf_handle: &ColumnFamily) -> DBRawIterator<'b> { let opts = ReadOptions::default(); DBRawIterator::new_cf(self, cf_handle, &opts) } /// Opens a raw iterator over the database, using the given read options - pub fn raw_iterator_opt(&self, readopts: &ReadOptions) -> DBRawIterator { + pub fn raw_iterator_opt<'a: 'b, 'b>(&'a self, readopts: &ReadOptions) -> DBRawIterator<'b> { DBRawIterator::new(self, readopts) } /// Opens a raw iterator over the given column family, using the given read options - pub fn raw_iterator_cf_opt( - &self, + pub fn raw_iterator_cf_opt<'a: 'b, 'b>( + &'a self, cf_handle: &ColumnFamily, readopts: &ReadOptions, - ) -> DBRawIterator { + ) -> DBRawIterator<'b> { DBRawIterator::new_cf(self, cf_handle, readopts) } diff --git a/tests/fail/iterator_outlive_db.rs b/tests/fail/iterator_outlive_db.rs new file mode 100644 index 0000000..49463f1 --- /dev/null +++ b/tests/fail/iterator_outlive_db.rs @@ -0,0 +1,8 @@ +use rocksdb::{IteratorMode, DB}; + +fn main() { + let _iter = { + let db = DB::open_default("foo").unwrap(); + db.iterator(IteratorMode::Start) + }; +} diff --git a/tests/fail/iterator_outlive_db.stderr b/tests/fail/iterator_outlive_db.stderr new file mode 100644 index 0000000..08b7d0d --- /dev/null +++ b/tests/fail/iterator_outlive_db.stderr @@ -0,0 +1,10 @@ +error[E0597]: `db` does not live long enough + --> $DIR/iterator_outlive_db.rs:6:9 + | +4 | let _iter = { + | ----- borrow later stored here +5 | let db = DB::open_default("foo").unwrap(); +6 | db.iterator(IteratorMode::Start) + | ^^ borrowed value does not live long enough +7 | }; + | - `db` dropped here while still borrowed diff --git a/tests/test_iterator.rs b/tests/test_iterator.rs index 39ee5b3..de417a3 100644 --- a/tests/test_iterator.rs +++ b/tests/test_iterator.rs @@ -299,7 +299,7 @@ fn test_full_iterator() { assert!(DB::destroy(&opts, path).is_ok()); } -fn custom_iter(db: &DB) -> impl Iterator { +fn custom_iter<'a>(db: &'a DB) -> impl Iterator + 'a { db.iterator(IteratorMode::Start) .map(|(_, db_value)| db_value.len()) } @@ -316,3 +316,9 @@ fn test_custom_iterator() { let opts = Options::default(); assert!(DB::destroy(&opts, path).is_ok()); } + +#[test] +fn test_iterator_outlive_db() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/fail/iterator_outlive_db.rs"); +}