Repository: paritytech/ink (rust)

Issue:
Implement core::iter::Extend for storage collections
Usually we use memory data structures as arguments and storage data structures, well, for contract storage. Given this assumption the following situation seems likely to arise:
```
fn new(&mut self, owners: Vec<AccountId>,) {
    for owner in &owners {
        self.owners.push(*owner)
    }
}
```
A simple ergonomic improvement would be to implement `Extend` for the storage structures. That would simplify the above example to:
```
fn new(&mut self, owners: Vec<AccountId>,) {
    self.owners.extend(owners);
}
```

Pull request description:
Implement core::iter::Extend for storage collections
Closes #333 

Patch:
diff --git a/core/src/storage/collections/binary_heap/impls.rs b/core/src/storage/collections/binary_heap/impls.rs
index 6f5b71ad06..060d6eecc3 100644
--- a/core/src/storage/collections/binary_heap/impls.rs
+++ b/core/src/storage/collections/binary_heap/impls.rs
@@ -440,3 +440,29 @@ where
         Iter::new(self)
     }
 }
+
+impl<T> Extend<T> for BinaryHeap<T>
+where
+    T: Codec + Ord,
+{
+    fn extend<I>(&mut self, iter: I)
+    where
+        I: IntoIterator<Item = T>,
+    {
+        for i in iter {
+            self.push(i)
+        }
+    }
+}
+
+impl<'a, T> Extend<&'a T> for BinaryHeap<T>
+where
+    T: Codec + Ord + Copy + 'a,
+{
+    fn extend<I>(&mut self, iter: I)
+    where
+        I: IntoIterator<Item = &'a T>,
+    {
+        self.extend(iter.into_iter().copied())
+    }
+}
diff --git a/core/src/storage/collections/bitvec/vec.rs b/core/src/storage/collections/bitvec/vec.rs
index e4f0054f29..c59a151e81 100644
--- a/core/src/storage/collections/bitvec/vec.rs
+++ b/core/src/storage/collections/bitvec/vec.rs
@@ -117,6 +117,20 @@ impl Drop for BitVec {
     }
 }
 
+impl Extend<bool> for BitVec {
+    fn extend<T: IntoIterator<Item = bool>>(&mut self, iter: T) {
+        for b in iter {
+            self.push(b)
+        }
+    }
+}
+
+impl<'a> Extend<&'a bool> for BitVec {
+    fn extend<T: IntoIterator<Item = &'a bool>>(&mut self, iter: T) {
+        self.extend(iter.into_iter().copied())
+    }
+}
+
 impl BitVec {
     /// Returns the number of bits.
     pub fn len(&self) -> u32 {
diff --git a/core/src/storage/collections/btree_map/impls.rs b/core/src/storage/collections/btree_map/impls.rs
index e9b2fbd651..175ae7b8ea 100644
--- a/core/src/storage/collections/btree_map/impls.rs
+++ b/core/src/storage/collections/btree_map/impls.rs
@@ -262,6 +262,38 @@ impl<K, V> AllocateUsing for BTreeMap<K, V> {
     }
 }
 
+impl<K, V> Extend<(K, V)> for BTreeMap<K, V>
+where
+    K: Codec + Ord,
+    V: Codec,
+{
+    fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
+        for (k, v) in iter {
+            self.insert(k, v);
+        }
+    }
+}
+
+impl<'a, K, V> Extend<(&'a K, &'a V)> for BTreeMap<K, V>
+where
+    K: Codec + Ord + Copy,
+    V: Codec + Copy,
+{
+    fn extend<I: IntoIterator<Item = (&'a K, &'a V)>>(&mut self, iter: I) {
+        self.extend(iter.into_iter().map(|(&key, &value)| (key, value)));
+    }
+}
+
+impl<'a, K, V> Extend<&'a (K, V)> for BTreeMap<K, V>
+where
+    K: Codec + Ord + Copy + 'a,
+    V: Codec + Copy + 'a,
+{
+    fn extend<I: IntoIterator<Item = &'a (K, V)>>(&mut self, iter: I) {
+        self.extend(iter.into_iter().copied());
+    }
+}
+
 impl<K, V> BTreeMap<K, V> {
     /// Returns the index of the root node.
     pub(super) fn root(&self) -> &Option<NodeHandle> {
diff --git a/core/src/storage/collections/hash_map/impls.rs b/core/src/storage/collections/hash_map/impls.rs
index 70e3a01d24..f5101b2964 100644
--- a/core/src/storage/collections/hash_map/impls.rs
+++ b/core/src/storage/collections/hash_map/impls.rs
@@ -194,6 +194,38 @@ impl<K, V> Initialize for HashMap<K, V> {
     }
 }
 
+impl<K, V> Extend<(K, V)> for HashMap<K, V>
+where
+    K: scale::Codec + Hash + Eq,
+    V: scale::Codec,
+{
+    fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
+        for (k, v) in iter {
+            self.insert(k, v);
+        }
+    }
+}
+
+impl<'a, K, V> Extend<(&'a K, &'a V)> for HashMap<K, V>
+where
+    K: scale::Codec + Hash + Eq + Copy,
+    V: scale::Codec + Copy,
+{
+    fn extend<I: IntoIterator<Item = (&'a K, &'a V)>>(&mut self, iter: I) {
+        self.extend(iter.into_iter().map(|(&key, &value)| (key, value)));
+    }
+}
+
+impl<'a, K, V> Extend<&'a (K, V)> for HashMap<K, V>
+where
+    K: scale::Codec + Hash + Eq + Copy + 'a,
+    V: scale::Codec + Copy + 'a,
+{
+    fn extend<I: IntoIterator<Item = &'a (K, V)>>(&mut self, iter: I) {
+        self.extend(iter.into_iter().copied());
+    }
+}
+
 impl<K, V> HashMap<K, V> {
     /// Returns the number of key-value pairs in the map.
     pub fn len(&self) -> u32 {
diff --git a/core/src/storage/collections/vec/impls.rs b/core/src/storage/collections/vec/impls.rs
index 85e74f735f..55e0c732f5 100644
--- a/core/src/storage/collections/vec/impls.rs
+++ b/core/src/storage/collections/vec/impls.rs
@@ -12,11 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use core::iter::{
-    DoubleEndedIterator,
-    ExactSizeIterator,
-};
-
 #[cfg(feature = "ink-generate-abi")]
 use ink_abi::{
     HasLayout,
@@ -149,6 +144,32 @@ where
     }
 }
 
+impl<T> Extend<T> for Vec<T>
+where
+    T: scale::Codec,
+{
+    fn extend<I>(&mut self, iter: I)
+    where
+        I: IntoIterator<Item = T>,
+    {
+        for i in iter {
+            self.push(i)
+        }
+    }
+}
+
+impl<'a, T> Extend<&'a T> for Vec<T>
+where
+    T: scale::Codec + Copy + 'a,
+{
+    fn extend<I>(&mut self, iter: I)
+    where
+        I: IntoIterator<Item = &'a T>,
+    {
+        self.extend(iter.into_iter().copied())
+    }
+}
+
 impl<T> scale::Encode for Vec<T> {
     fn encode_to<W: scale::Output>(&self, dest: &mut W) {
         self.len.encode_to(dest);


Test patch:
diff --git a/core/src/storage/collections/binary_heap/tests.rs b/core/src/storage/collections/binary_heap/tests.rs
index 6446e7c47d..a0ef1a4b16 100644
--- a/core/src/storage/collections/binary_heap/tests.rs
+++ b/core/src/storage/collections/binary_heap/tests.rs
@@ -28,6 +28,7 @@ use core::{
     cmp::Ord,
     fmt::Debug,
 };
+use ink_prelude::collections::HashSet;
 use ink_primitives::Key;
 use scale::{
     Codec,
@@ -346,3 +347,26 @@ fn min_heap_with_multiple_levels() -> Result<()> {
         Ok(())
     })
 }
+
+#[test]
+fn extend() -> Result<()> {
+    env::test::run_test::<env::DefaultEnvTypes, _>(|_| {
+        // given
+        let mut heap = filled_heap();
+        let arr = [1, 2, 3];
+
+        // when
+        heap.extend(&arr);
+
+        // then
+        let mut expected = HashSet::new();
+        expected.extend(heap.values());
+        expected.extend(arr.iter());
+
+        let actual = heap.values().collect::<HashSet<_>>();
+
+        assert_eq!(actual, expected);
+
+        Ok(())
+    })
+}
diff --git a/core/src/storage/collections/bitvec/tests.rs b/core/src/storage/collections/bitvec/tests.rs
index 806374d71a..62c34433dc 100644
--- a/core/src/storage/collections/bitvec/tests.rs
+++ b/core/src/storage/collections/bitvec/tests.rs
@@ -115,6 +115,20 @@ fn push_filled() {
     assert_eq!(filled.len(), len + 1);
 }
 
+#[test]
+fn extend() {
+    let mut filled = new_filled_bitvec();
+    let arr = [true, false, true];
+
+    let mut expected = Vec::new();
+    expected.extend(filled.iter());
+    expected.extend(&arr);
+
+    filled.extend(&arr);
+
+    assert!(filled.iter().eq(expected.iter().cloned()));
+}
+
 #[test]
 fn pop_empty() {
     assert_eq!(new_empty_bitvec().pop(), None);
diff --git a/core/src/storage/collections/btree_map/tests.rs b/core/src/storage/collections/btree_map/tests.rs
index f7d5c45cc2..7b1dc8ee7c 100644
--- a/core/src/storage/collections/btree_map/tests.rs
+++ b/core/src/storage/collections/btree_map/tests.rs
@@ -34,6 +34,7 @@ use crate::{
         BTreeMap,
     },
 };
+use ink_prelude::collections::HashMap;
 use ink_primitives::Key;
 use itertools::Itertools;
 
@@ -442,3 +443,29 @@ fn complex_trees_work() -> Result<()> {
         Ok(())
     })
 }
+
+#[test]
+fn extend_works() -> Result<()> {
+    env::test::run_test::<env::DefaultEnvTypes, _>(|_| {
+        // given
+        let arr1 = [(1i32, 2i32), (3, 4), (5, 6)];
+        let arr2 = [(7i32, 8i32), (9, 10)];
+        let mut map = empty_map();
+
+        let mut expected = HashMap::new();
+
+        expected.extend(arr1.iter().cloned());
+        expected.extend(arr2.iter().cloned());
+
+        // when
+        map.extend(arr1.iter());
+        map.extend(arr2.iter());
+
+        // then
+        assert_eq!(map.len() as usize, expected.len());
+        for (k, v) in &expected {
+            assert_eq!(Some(v), map.get(k))
+        }
+        Ok(())
+    })
+}
diff --git a/core/src/storage/collections/hash_map/tests.rs b/core/src/storage/collections/hash_map/tests.rs
index 141505acaa..0be8214c6f 100644
--- a/core/src/storage/collections/hash_map/tests.rs
+++ b/core/src/storage/collections/hash_map/tests.rs
@@ -188,3 +188,29 @@ fn mutate_with() -> Result<()> {
         Ok(())
     })
 }
+
+#[test]
+fn extend_works() -> Result<()> {
+    env::test::run_test::<env::DefaultEnvTypes, _>(|_| {
+        // given
+        let arr1 = [(1i32, 2i32), (3, 4), (5, 6)];
+        let arr2 = [(7i32, 8i32), (9, 10)];
+        let mut map = new_empty::<i32, i32>();
+
+        let mut expected = ink_prelude::collections::HashMap::new();
+
+        expected.extend(arr1.iter().cloned());
+        expected.extend(arr2.iter().cloned());
+
+        // when
+        map.extend(arr1.iter());
+        map.extend(arr2.iter());
+
+        // then
+        assert_eq!(map.len() as usize, expected.len());
+        for (k, v) in &expected {
+            assert_eq!(Some(v), map.get(k))
+        }
+        Ok(())
+    })
+}
diff --git a/core/src/storage/collections/vec/tests.rs b/core/src/storage/collections/vec/tests.rs
index 3ea56d8ec0..c1e9fc9d50 100644
--- a/core/src/storage/collections/vec/tests.rs
+++ b/core/src/storage/collections/vec/tests.rs
@@ -241,6 +241,20 @@ fn iter_size_hint() {
     assert_eq!(iter.size_hint(), (3, Some(3)));
 }
 
+#[test]
+fn extend() {
+    let mut vec1 = new_filled_vec();
+    let arr = [1, 2, 3];
+
+    let mut expected = ink_prelude::vec::Vec::new();
+    expected.extend(vec1.iter());
+    expected.extend(&arr);
+
+    vec1.extend(&arr);
+
+    assert!(vec1.iter().eq(expected.iter()));
+}
+
 #[test]
 fn regression_issue_193() {
     let mut vec = new_empty_vec();