diff --git a/src/btreemap.rs b/src/btreemap.rs index 8561771..86c7e20 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -5,6 +5,8 @@ use std::collections::BTreeMap; use std::iter::{FromIterator, IntoIterator}; use std::ops::{Index, IndexMut}; +use crate::DefaultFn; + /// A `BTreeMap` that returns a default when keys are accessed that are not present. #[derive(Clone, Debug)] #[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] @@ -13,7 +15,7 @@ pub struct DefaultBTreeMap { default: V, #[debug(skip)] #[cfg_attr(feature = "with-serde", serde(skip))] - default_fn: Box>, + default_fn: Box>, } impl PartialEq for DefaultBTreeMap { @@ -108,6 +110,31 @@ impl DefaultBTreeMap { pub fn get_default(&self) -> V { self.default_fn.call() } + + /// Creates an empty `DefaultBTreeMap` with `default_fn` as the default value generation + /// function for missing keys. When the provided `default_fn` only calls clone on a value, + /// using `DefaultBTreeMap::new` is preferred. + pub fn from_fn(default_fn: impl DefaultFn + 'static) -> DefaultBTreeMap { + DefaultBTreeMap { + map: BTreeMap::new(), + default: default_fn.call(), + default_fn: Box::new(default_fn), + } + } + + /// Creates a `DefaultBTreeMap` based on an existing map and using `default_fn` as the default + /// value generation function for missing keys. When the provided `default_fn` is equivalent to + /// V::default(), then using `DefaultBTreeMap::from(map)` is preferred. + pub fn from_map_and_fn( + map: BTreeMap, + default_fn: impl DefaultFn + 'static, + ) -> DefaultBTreeMap { + DefaultBTreeMap { + map, + default: default_fn.call(), + default_fn: Box::new(default_fn), + } + } } impl DefaultBTreeMap { @@ -424,6 +451,27 @@ mod tests { assert_eq!(expected, default.into()); } + #[test] + fn from_fn() { + let i: i32 = 20; + let mut map = DefaultBTreeMap::from_fn(move || i); + map[0] += 1; + assert_eq!(21, map[0]); + assert_eq!(20, map[1]); + + } + + #[test] + fn from_map_and_fn() { + let i: i32 = 20; + let normal: BTreeMap = vec![(0, 1), (2, 3)].into_iter().collect(); + let mut map = DefaultBTreeMap::from_map_and_fn(normal, move || i); + map[0] += 1; + assert_eq!(map[0], 2); + assert_eq!(map[1], 20); + assert_eq!(map[2], 3); + } + #[cfg(feature = "with-serde")] mod serde_tests { use super::*; diff --git a/src/hashmap.rs b/src/hashmap.rs index 5eca6dc..12fb432 100644 --- a/src/hashmap.rs +++ b/src/hashmap.rs @@ -6,6 +6,8 @@ use std::hash::Hash; use std::iter::{FromIterator, IntoIterator}; use std::ops::{Index, IndexMut}; +use crate::DefaultFn; + /// A `HashMap` that returns a default when keys are accessed that are not present. #[derive(Clone, Debug)] #[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] @@ -14,7 +16,7 @@ pub struct DefaultHashMap { default: V, #[debug(skip)] #[cfg_attr(feature = "with-serde", serde(skip))] - default_fn: Box>, + default_fn: Box>, } impl PartialEq for DefaultHashMap { @@ -109,6 +111,31 @@ impl DefaultHashMap { pub fn get_default(&self) -> V { self.default_fn.call() } + + /// Creates an empty `DefaultHashMap` with `default_fn` as the default value generation + /// function for missing keys. When the provided `default_fn` only calls clone on a value, + /// using `DefaultHashMap::new` is preferred. + pub fn from_fn(default_fn: impl DefaultFn + 'static) -> DefaultHashMap { + DefaultHashMap { + map: HashMap::new(), + default: default_fn.call(), + default_fn: Box::new(default_fn), + } + } + + /// Creates a `DefaultHashMap` based on an existing map and using `default_fn` as the default + /// value generation function for missing keys. When the provided `default_fn` is equivalent to + /// V::default(), then using `DefaultHashMap::from(map)` is preferred. + pub fn from_map_and_fn( + map: HashMap, + default_fn: impl DefaultFn + 'static, + ) -> DefaultHashMap { + DefaultHashMap { + map, + default: default_fn.call(), + default_fn: Box::new(default_fn), + } + } } impl DefaultHashMap { @@ -441,6 +468,27 @@ mod tests { assert_eq!(expected, default.into()); } + #[test] + fn from_fn() { + let i: i32 = 20; + let mut map = DefaultHashMap::from_fn(move || i); + map[0] += 1; + assert_eq!(21, map[0]); + assert_eq!(20, map[1]); + + } + + #[test] + fn from_map_and_fn() { + let i: i32 = 20; + let normal: HashMap = vec![(0, 1), (2, 3)].into_iter().collect(); + let mut map = DefaultHashMap::from_map_and_fn(normal, move || i); + map[0] += 1; + assert_eq!(map[0], 2); + assert_eq!(map[1], 20); + assert_eq!(map[2], 3); + } + #[cfg(feature = "with-serde")] mod serde_tests { use super::*;