Repository: aya-rs/aya (language: rust)

Issue:
Convert BPF maps to using enums 
Following what we currenlty [do for EBPF Programs ](https://github.com/aya-rs/aya/blob/main/aya/src/programs/mod.rs#L221) convert aya's notion of bpf maps to be an Enum rather than having concrete types (HashMap, PerfEventArray etc) for every BPF map type. 

Maps were initially designed like this  to allow users to implement their own maps, however now since we implement most of the ebpf API it makes more sense to switch to enums and simply support it all.  


Discussion / hints:
@dave-tucker You can assign me here :) 
I've started this in https://github.com/astoycos/aya/tree/refactor-map Still have some to go but I'll keep updating with status here

Fix patch:
diff --git a/aya-log/src/lib.rs b/aya-log/src/lib.rs
index b56c16e6d..a2dd686b3 100644
--- a/aya-log/src/lib.rs
+++ b/aya-log/src/lib.rs
@@ -90,7 +90,7 @@ impl BpfLogger {
         logger: T,
     ) -> Result<BpfLogger, Error> {
         let logger = Arc::new(logger);
-        let mut logs: AsyncPerfEventArray<_> = bpf.map_mut("AYA_LOGS")?.try_into()?;
+        let mut logs: AsyncPerfEventArray<_> = bpf.take_map("AYA_LOGS").unwrap().try_into()?;
 
         for cpu_id in online_cpus().map_err(Error::InvalidOnlineCpu)? {
             let mut buf = logs.open(cpu_id, None)?;
diff --git a/aya/Cargo.toml b/aya/Cargo.toml
index 8c43a401b..c5b2f01d2 100644
--- a/aya/Cargo.toml
+++ b/aya/Cargo.toml
@@ -34,4 +34,4 @@ async_std = ["async-io", "async"]
 
 [package.metadata.docs.rs]
 all-features = true
-rustdoc-args = ["--cfg", "docsrs"]
+rustdoc-args = ["--cfg", "docsrs","-D", "warnings"]
diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs
index db1ad3be7..f5307f83c 100644
--- a/aya/src/bpf.rs
+++ b/aya/src/bpf.rs
@@ -13,10 +13,10 @@ use thiserror::Error;
 
 use crate::{
     generated::{
-        bpf_map_type::BPF_MAP_TYPE_PERF_EVENT_ARRAY, AYA_PERF_EVENT_IOC_DISABLE,
-        AYA_PERF_EVENT_IOC_ENABLE, AYA_PERF_EVENT_IOC_SET_BPF,
+        bpf_map_type, bpf_map_type::*, AYA_PERF_EVENT_IOC_DISABLE, AYA_PERF_EVENT_IOC_ENABLE,
+        AYA_PERF_EVENT_IOC_SET_BPF,
     },
-    maps::{Map, MapError, MapLock, MapRef, MapRefMut},
+    maps::{Map, MapData, MapError},
     obj::{
         btf::{Btf, BtfError},
         MapKind, Object, ParseError, ProgramSection,
@@ -451,7 +451,7 @@ impl<'a> BpfLoader<'a> {
                     }
                 }
             }
-            let mut map = Map {
+            let mut map = MapData {
                 obj,
                 fd: None,
                 pinned: false,
@@ -638,14 +638,43 @@ impl<'a> BpfLoader<'a> {
                 (name, program)
             })
             .collect();
-        let maps = maps
-            .drain()
-            .map(|(name, map)| (name, MapLock::new(map)))
-            .collect();
-        Ok(Bpf { maps, programs })
+        let maps: Result<HashMap<String, Map>, BpfError> = maps.drain().map(parse_map).collect();
+
+        Ok(Bpf {
+            maps: maps?,
+            programs,
+        })
     }
 }
 
+fn parse_map(data: (String, MapData)) -> Result<(String, Map), BpfError> {
+    let name = data.0;
+    let map = data.1;
+    let map_type = bpf_map_type::try_from(map.obj.map_type())?;
+    let map = match map_type {
+        BPF_MAP_TYPE_ARRAY => Ok(Map::Array(map)),
+        BPF_MAP_TYPE_PERCPU_ARRAY => Ok(Map::PerCpuArray(map)),
+        BPF_MAP_TYPE_PROG_ARRAY => Ok(Map::ProgramArray(map)),
+        BPF_MAP_TYPE_HASH => Ok(Map::HashMap(map)),
+        BPF_MAP_TYPE_PERCPU_HASH => Ok(Map::PerCpuHashMap(map)),
+        BPF_MAP_TYPE_PERF_EVENT_ARRAY | BPF_MAP_TYPE_LRU_PERCPU_HASH => {
+            Ok(Map::PerfEventArray(map))
+        }
+        BPF_MAP_TYPE_SOCKHASH => Ok(Map::SockHash(map)),
+        BPF_MAP_TYPE_SOCKMAP => Ok(Map::SockMap(map)),
+        BPF_MAP_TYPE_BLOOM_FILTER => Ok(Map::BloomFilter(map)),
+        BPF_MAP_TYPE_LPM_TRIE => Ok(Map::LpmTrie(map)),
+        BPF_MAP_TYPE_STACK => Ok(Map::Stack(map)),
+        BPF_MAP_TYPE_STACK_TRACE => Ok(Map::StackTraceMap(map)),
+        BPF_MAP_TYPE_QUEUE => Ok(Map::Queue(map)),
+        m => Err(BpfError::MapError(MapError::InvalidMapType {
+            map_type: m as u32,
+        })),
+    }?;
+
+    Ok((name, map))
+}
+
 impl<'a> Default for BpfLoader<'a> {
     fn default() -> Self {
         BpfLoader::new()
@@ -655,7 +684,7 @@ impl<'a> Default for BpfLoader<'a> {
 /// The main entry point into the library, used to work with eBPF programs and maps.
 #[derive(Debug)]
 pub struct Bpf {
-    maps: HashMap<String, MapLock>,
+    maps: HashMap<String, Map>,
     programs: HashMap<String, Program>,
 }
 
@@ -714,22 +743,8 @@ impl Bpf {
     ///
     /// For more details and examples on maps and their usage, see the [maps module
     /// documentation][crate::maps].
-    ///
-    /// # Errors
-    ///
-    /// Returns [`MapError::MapNotFound`] if the map does not exist. If the map is already borrowed
-    /// mutably with [map_mut](Self::map_mut) then [`MapError::BorrowError`] is returned.
-    pub fn map(&self, name: &str) -> Result<MapRef, MapError> {
-        self.maps
-            .get(name)
-            .ok_or_else(|| MapError::MapNotFound {
-                name: name.to_owned(),
-            })
-            .and_then(|lock| {
-                lock.try_read().map_err(|_| MapError::BorrowError {
-                    name: name.to_owned(),
-                })
-            })
+    pub fn map(&self, name: &str) -> Option<&Map> {
+        self.maps.get(name)
     }
 
     /// Returns a mutable reference to the map with the given name.
@@ -739,22 +754,24 @@ impl Bpf {
     ///
     /// For more details and examples on maps and their usage, see the [maps module
     /// documentation][crate::maps].
+    pub fn map_mut(&mut self, name: &str) -> Option<&mut Map> {
+        self.maps.get_mut(name)
+    }
+
+    /// Takes ownership of a map with the given name.
     ///
-    /// # Errors
+    /// Use this when borrowing with [`map`](crate::Bpf::map) or [`map_mut`](crate::Bpf::map_mut)
+    /// is not possible (eg when using the map from an async task). The returned
+    /// map will be closed on `Drop`, therefore the caller is responsible for
+    /// managing its lifetime.
     ///
-    /// Returns [`MapError::MapNotFound`] if the map does not exist. If the map is already borrowed
-    /// mutably with [map_mut](Self::map_mut) then [`MapError::BorrowError`] is returned.
-    pub fn map_mut(&self, name: &str) -> Result<MapRefMut, MapError> {
-        self.maps
-            .get(name)
-            .ok_or_else(|| MapError::MapNotFound {
-                name: name.to_owned(),
-            })
-            .and_then(|lock| {
-                lock.try_write().map_err(|_| MapError::BorrowError {
-                    name: name.to_owned(),
-                })
-            })
+    /// The returned type is mostly opaque. In order to do anything useful with it you need to
+    /// convert it to a [typed map](crate::maps).
+    ///
+    /// For more details and examples on maps and their usage, see the [maps module
+    /// documentation][crate::maps].
+    pub fn take_map(&mut self, name: &str) -> Option<Map> {
+        self.maps.remove(name)
     }
 
     /// An iterator over all the maps.
@@ -764,22 +781,14 @@ impl Bpf {
     /// # let mut bpf = aya::Bpf::load(&[])?;
     /// for (name, map) in bpf.maps() {
     ///     println!(
-    ///         "found map `{}` of type `{:?}`",
+    ///         "found map `{}`",
     ///         name,
-    ///         map?.map_type().unwrap()
     ///     );
     /// }
     /// # Ok::<(), aya::BpfError>(())
     /// ```
-    pub fn maps(&self) -> impl Iterator<Item = (&str, Result<MapRef, MapError>)> {
-        let ret = self.maps.iter().map(|(name, lock)| {
-            (
-                name.as_str(),
-                lock.try_read()
-                    .map_err(|_| MapError::BorrowError { name: name.clone() }),
-            )
-        });
-        ret
+    pub fn maps(&self) -> impl Iterator<Item = (&str, &Map)> {
+        self.maps.iter().map(|(name, map)| (name.as_str(), map))
     }
 
     /// Returns a reference to the program with the given name.
diff --git a/aya/src/maps/array/array.rs b/aya/src/maps/array/array.rs
index ac5c65871..1402d57d5 100644
--- a/aya/src/maps/array/array.rs
+++ b/aya/src/maps/array/array.rs
@@ -1,12 +1,10 @@
 use std::{
+    convert::{AsMut, AsRef},
     marker::PhantomData,
-    mem,
-    ops::{Deref, DerefMut},
 };
 
 use crate::{
-    generated::bpf_map_type::BPF_MAP_TYPE_ARRAY,
-    maps::{IterableMap, Map, MapError, MapRef, MapRefMut},
+    maps::{check_bounds, check_kv_size, IterableMap, MapData, MapError},
     sys::{bpf_map_lookup_elem, bpf_map_update_elem},
     Pod,
 };
@@ -22,38 +20,26 @@ use crate::{
 ///
 /// # Examples
 /// ```no_run
-/// # let bpf = aya::Bpf::load(&[])?;
+/// # let mut bpf = aya::Bpf::load(&[])?;
 /// use aya::maps::Array;
 ///
-/// let mut array = Array::try_from(bpf.map_mut("ARRAY")?)?;
+/// let mut array = Array::try_from(bpf.map_mut("ARRAY").unwrap())?;
 /// array.set(1, 42, 0)?;
 /// assert_eq!(array.get(&1, 0)?, 42);
 /// # Ok::<(), aya::BpfError>(())
 /// ```
 #[doc(alias = "BPF_MAP_TYPE_ARRAY")]
-pub struct Array<T: Deref<Target = Map>, V: Pod> {
+pub struct Array<T, V: Pod> {
     inner: T,
     _v: PhantomData<V>,
 }
 
-impl<T: Deref<Target = Map>, V: Pod> Array<T, V> {
-    fn new(map: T) -> Result<Array<T, V>, MapError> {
-        let map_type = map.obj.map_type();
-        if map_type != BPF_MAP_TYPE_ARRAY as u32 {
-            return Err(MapError::InvalidMapType { map_type });
-        }
-        let expected = mem::size_of::<u32>();
-        let size = map.obj.key_size() as usize;
-        if size != expected {
-            return Err(MapError::InvalidKeySize { size, expected });
-        }
+impl<T: AsRef<MapData>, V: Pod> Array<T, V> {
+    pub(crate) fn new(map: T) -> Result<Array<T, V>, MapError> {
+        let data = map.as_ref();
+        check_kv_size::<u32, V>(data)?;
 
-        let expected = mem::size_of::<V>();
-        let size = map.obj.value_size() as usize;
-        if size != expected {
-            return Err(MapError::InvalidValueSize { size, expected });
-        }
-        let _fd = map.fd_or_err()?;
+        let _fd = data.fd_or_err()?;
 
         Ok(Array {
             inner: map,
@@ -65,7 +51,7 @@ impl<T: Deref<Target = Map>, V: Pod> Array<T, V> {
     ///
     /// This corresponds to the value of `bpf_map_def::max_entries` on the eBPF side.
     pub fn len(&self) -> u32 {
-        self.inner.obj.max_entries()
+        self.inner.as_ref().obj.max_entries()
     }
 
     /// Returns the value stored at the given index.
@@ -75,8 +61,9 @@ impl<T: Deref<Target = Map>, V: Pod> Array<T, V> {
     /// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`]
     /// if `bpf_map_lookup_elem` fails.
     pub fn get(&self, index: &u32, flags: u64) -> Result<V, MapError> {
-        self.check_bounds(*index)?;
-        let fd = self.inner.fd_or_err()?;
+        let data = self.inner.as_ref();
+        check_bounds(data, *index)?;
+        let fd = data.fd_or_err()?;
 
         let value = bpf_map_lookup_elem(fd, index, flags).map_err(|(_, io_error)| {
             MapError::SyscallError {
@@ -92,18 +79,9 @@ impl<T: Deref<Target = Map>, V: Pod> Array<T, V> {
     pub fn iter(&self) -> impl Iterator<Item = Result<V, MapError>> + '_ {
         (0..self.len()).map(move |i| self.get(&i, 0))
     }
-
-    fn check_bounds(&self, index: u32) -> Result<(), MapError> {
-        let max_entries = self.inner.obj.max_entries();
-        if index >= self.inner.obj.max_entries() {
-            Err(MapError::OutOfBounds { index, max_entries })
-        } else {
-            Ok(())
-        }
-    }
 }
 
-impl<T: Deref<Target = Map> + DerefMut<Target = Map>, V: Pod> Array<T, V> {
+impl<T: AsMut<MapData>, V: Pod> Array<T, V> {
     /// Sets the value of the element at the given index.
     ///
     /// # Errors
@@ -111,8 +89,9 @@ impl<T: Deref<Target = Map> + DerefMut<Target = Map>, V: Pod> Array<T, V> {
     /// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`]
     /// if `bpf_map_update_elem` fails.
     pub fn set(&mut self, index: u32, value: V, flags: u64) -> Result<(), MapError> {
-        let fd = self.inner.fd_or_err()?;
-        self.check_bounds(index)?;
+        let data = self.inner.as_mut();
+        check_bounds(data, index)?;
+        let fd = data.fd_or_err()?;
         bpf_map_update_elem(fd, Some(&index), &value, flags).map_err(|(_, io_error)| {
             MapError::SyscallError {
                 call: "bpf_map_update_elem".to_owned(),
@@ -123,28 +102,12 @@ impl<T: Deref<Target = Map> + DerefMut<Target = Map>, V: Pod> Array<T, V> {
     }
 }
 
-impl<T: Deref<Target = Map>, V: Pod> IterableMap<u32, V> for Array<T, V> {
-    fn map(&self) -> &Map {
-        &self.inner
+impl<T: AsRef<MapData>, V: Pod> IterableMap<u32, V> for Array<T, V> {
+    fn map(&self) -> &MapData {
+        self.inner.as_ref()
     }
 
     fn get(&self, index: &u32) -> Result<V, MapError> {
         self.get(index, 0)
     }
 }
-
-impl<V: Pod> TryFrom<MapRef> for Array<MapRef, V> {
-    type Error = MapError;
-
-    fn try_from(a: MapRef) -> Result<Array<MapRef, V>, MapError> {
-        Array::new(a)
-    }
-}
-
-impl<V: Pod> TryFrom<MapRefMut> for Array<MapRefMut, V> {
-    type Error = MapError;
-
-    fn try_from(a: MapRefMut) -> Result<Array<MapRefMut, V>, MapError> {
-        Array::new(a)
-    }
-}
diff --git a/aya/src/maps/array/mod.rs b/aya/src/maps/array/mod.rs
index 5b762e274..728e48512 100644
--- a/aya/src/maps/array/mod.rs
+++ b/aya/src/maps/array/mod.rs
@@ -4,6 +4,6 @@ mod array;
 mod per_cpu_array;
 mod program_array;
 
-pub use array::Array;
+pub use array::*;
 pub use per_cpu_array::PerCpuArray;
 pub use program_array::ProgramArray;
diff --git a/aya/src/maps/array/per_cpu_array.rs b/aya/src/maps/array/per_cpu_array.rs
index 6cbfb37b3..a3b5eab91 100644
--- a/aya/src/maps/array/per_cpu_array.rs
+++ b/aya/src/maps/array/per_cpu_array.rs
@@ -1,12 +1,10 @@
 use std::{
+    convert::{AsMut, AsRef},
     marker::PhantomData,
-    mem,
-    ops::{Deref, DerefMut},
 };
 
 use crate::{
-    generated::bpf_map_type::BPF_MAP_TYPE_PERCPU_ARRAY,
-    maps::{IterableMap, Map, MapError, MapRef, MapRefMut, PerCpuValues},
+    maps::{check_bounds, check_kv_size, IterableMap, MapData, MapError, PerCpuValues},
     sys::{bpf_map_lookup_elem_per_cpu, bpf_map_update_elem_per_cpu},
     Pod,
 };
@@ -31,11 +29,11 @@ use crate::{
 /// #     #[error(transparent)]
 /// #     Bpf(#[from] aya::BpfError)
 /// # }
-/// # let bpf = aya::Bpf::load(&[])?;
+/// # let mut bpf = aya::Bpf::load(&[])?;
 /// use aya::maps::{PerCpuArray, PerCpuValues};
 /// use aya::util::nr_cpus;
 ///
-/// let mut array = PerCpuArray::try_from(bpf.map_mut("ARRAY")?)?;
+/// let mut array = PerCpuArray::try_from(bpf.map_mut("ARRAY").unwrap())?;
 ///
 /// // set array[1] = 42 for all cpus
 /// let nr_cpus = nr_cpus()?;
@@ -50,29 +48,17 @@ use crate::{
 /// # Ok::<(), Error>(())
 /// ```
 #[doc(alias = "BPF_MAP_TYPE_PERCPU_ARRAY")]
-pub struct PerCpuArray<T: Deref<Target = Map>, V: Pod> {
+pub struct PerCpuArray<T, V: Pod> {
     inner: T,
     _v: PhantomData<V>,
 }
 
-impl<T: Deref<Target = Map>, V: Pod> PerCpuArray<T, V> {
-    fn new(map: T) -> Result<PerCpuArray<T, V>, MapError> {
-        let map_type = map.obj.map_type();
-        if map_type != BPF_MAP_TYPE_PERCPU_ARRAY as u32 {
-            return Err(MapError::InvalidMapType { map_type });
-        }
-        let expected = mem::size_of::<u32>();
-        let size = map.obj.key_size() as usize;
-        if size != expected {
-            return Err(MapError::InvalidKeySize { size, expected });
-        }
+impl<T: AsRef<MapData>, V: Pod> PerCpuArray<T, V> {
+    pub(crate) fn new(map: T) -> Result<PerCpuArray<T, V>, MapError> {
+        let data = map.as_ref();
+        check_kv_size::<u32, V>(data)?;
 
-        let expected = mem::size_of::<V>();
-        let size = map.obj.value_size() as usize;
-        if size != expected {
-            return Err(MapError::InvalidValueSize { size, expected });
-        }
-        let _fd = map.fd_or_err()?;
+        let _fd = data.fd_or_err()?;
 
         Ok(PerCpuArray {
             inner: map,
@@ -84,7 +70,7 @@ impl<T: Deref<Target = Map>, V: Pod> PerCpuArray<T, V> {
     ///
     /// This corresponds to the value of `bpf_map_def::max_entries` on the eBPF side.
     pub fn len(&self) -> u32 {
-        self.inner.obj.max_entries()
+        self.inner.as_ref().obj.max_entries()
     }
 
     /// Returns a slice of values - one for each CPU - stored at the given index.
@@ -94,8 +80,9 @@ impl<T: Deref<Target = Map>, V: Pod> PerCpuArray<T, V> {
     /// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`]
     /// if `bpf_map_lookup_elem` fails.
     pub fn get(&self, index: &u32, flags: u64) -> Result<PerCpuValues<V>, MapError> {
-        self.check_bounds(*index)?;
-        let fd = self.inner.fd_or_err()?;
+        let data = self.inner.as_ref();
+        check_bounds(data, *index)?;
+        let fd = data.fd_or_err()?;
 
         let value = bpf_map_lookup_elem_per_cpu(fd, index, flags).map_err(|(_, io_error)| {
             MapError::SyscallError {
@@ -111,18 +98,9 @@ impl<T: Deref<Target = Map>, V: Pod> PerCpuArray<T, V> {
     pub fn iter(&self) -> impl Iterator<Item = Result<PerCpuValues<V>, MapError>> + '_ {
         (0..self.len()).map(move |i| self.get(&i, 0))
     }
-
-    fn check_bounds(&self, index: u32) -> Result<(), MapError> {
-        let max_entries = self.inner.obj.max_entries();
-        if index >= self.inner.obj.max_entries() {
-            Err(MapError::OutOfBounds { index, max_entries })
-        } else {
-            Ok(())
-        }
-    }
 }
 
-impl<T: Deref<Target = Map> + DerefMut<Target = Map>, V: Pod> PerCpuArray<T, V> {
+impl<T: AsMut<MapData>, V: Pod> PerCpuArray<T, V> {
     /// Sets the values - one for each CPU - at the given index.
     ///
     /// # Errors
@@ -130,8 +108,10 @@ impl<T: Deref<Target = Map> + DerefMut<Target = Map>, V: Pod> PerCpuArray<T, V>
     /// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`]
     /// if `bpf_map_update_elem` fails.
     pub fn set(&mut self, index: u32, values: PerCpuValues<V>, flags: u64) -> Result<(), MapError> {
-        let fd = self.inner.fd_or_err()?;
-        self.check_bounds(index)?;
+        let data = self.inner.as_mut();
+        check_bounds(data, index)?;
+        let fd = data.fd_or_err()?;
+
         bpf_map_update_elem_per_cpu(fd, &index, &values, flags).map_err(|(_, io_error)| {
             MapError::SyscallError {
                 call: "bpf_map_update_elem".to_owned(),
@@ -142,28 +122,12 @@ impl<T: Deref<Target = Map> + DerefMut<Target = Map>, V: Pod> PerCpuArray<T, V>
     }
 }
 
-impl<T: Deref<Target = Map>, V: Pod> IterableMap<u32, PerCpuValues<V>> for PerCpuArray<T, V> {
-    fn map(&self) -> &Map {
-        &self.inner
+impl<T: AsRef<MapData>, V: Pod> IterableMap<u32, PerCpuValues<V>> for PerCpuArray<T, V> {
+    fn map(&self) -> &MapData {
+        self.inner.as_ref()
     }
 
     fn get(&self, index: &u32) -> Result<PerCpuValues<V>, MapError> {
         self.get(index, 0)
     }
 }
-
-impl<V: Pod> TryFrom<MapRef> for PerCpuArray<MapRef, V> {
-    type Error = MapError;
-
-    fn try_from(a: MapRef) -> Result<PerCpuArray<MapRef, V>, MapError> {
-        PerCpuArray::new(a)
-    }
-}
-
-impl<V: Pod> TryFrom<MapRefMut> for PerCpuArray<MapRefMut, V> {
-    type Error = MapError;
-
-    fn try_from(a: MapRefMut) -> Result<PerCpuArray<MapRefMut, V>, MapError> {
-        PerCpuArray::new(a)
-    }
-}
diff --git a/aya/src/maps/array/program_array.rs b/aya/src/maps/array/program_array.rs
index aa5372cce..27566e286 100644
--- a/aya/src/maps/array/program_array.rs
+++ b/aya/src/maps/array/program_array.rs
@@ -1,14 +1,12 @@
 //! An array of eBPF program file descriptors used as a jump table.
 
 use std::{
-    mem,
-    ops::{Deref, DerefMut},
+    convert::{AsMut, AsRef},
     os::unix::prelude::{AsRawFd, RawFd},
 };
 
 use crate::{
-    generated::bpf_map_type::BPF_MAP_TYPE_PROG_ARRAY,
-    maps::{Map, MapError, MapKeys, MapRef, MapRefMut},
+    maps::{check_bounds, check_kv_size, MapData, MapError, MapKeys},
     programs::ProgramFd,
     sys::{bpf_map_delete_elem, bpf_map_update_elem},
 };
@@ -29,7 +27,7 @@ use crate::{
 /// use aya::maps::ProgramArray;
 /// use aya::programs::CgroupSkb;
 ///
-/// let mut prog_array = ProgramArray::try_from(bpf.map_mut("JUMP_TABLE")?)?;
+/// let mut prog_array = ProgramArray::try_from(bpf.take_map("JUMP_TABLE").unwrap())?;
 /// let prog_0: &CgroupSkb = bpf.program("example_prog_0").unwrap().try_into()?;
 /// let prog_0_fd =  prog_0.fd().unwrap();
 /// let prog_1: &CgroupSkb = bpf.program("example_prog_1").unwrap().try_into()?;
@@ -49,28 +47,16 @@ use crate::{
 /// # Ok::<(), aya::BpfError>(())
 /// ```
 #[doc(alias = "BPF_MAP_TYPE_PROG_ARRAY")]
-pub struct ProgramArray<T: Deref<Target = Map>> {
+pub struct ProgramArray<T> {
     inner: T,
 }
 
-impl<T: Deref<Target = Map>> ProgramArray<T> {
-    fn new(map: T) -> Result<ProgramArray<T>, MapError> {
-        let map_type = map.obj.map_type();
-        if map_type != BPF_MAP_TYPE_PROG_ARRAY as u32 {
-            return Err(MapError::InvalidMapType { map_type });
-        }
-        let expected = mem::size_of::<u32>();
-        let size = map.obj.key_size() as usize;
-        if size != expected {
-            return Err(MapError::InvalidKeySize { size, expected });
-        }
+impl<T: AsRef<MapData>> ProgramArray<T> {
+    pub(crate) fn new(map: T) -> Result<ProgramArray<T>, MapError> {
+        let data = map.as_ref();
+        check_kv_size::<u32, RawFd>(data)?;
 
-        let expected = mem::size_of::<RawFd>();
-        let size = map.obj.value_size() as usize;
-        if size != expected {
-            return Err(MapError::InvalidValueSize { size, expected });
-        }
-        let _fd = map.fd_or_err()?;
+        let _fd = data.fd_or_err()?;
 
         Ok(ProgramArray { inner: map })
     }
@@ -78,27 +64,19 @@ impl<T: Deref<Target = Map>> ProgramArray<T> {
     /// An iterator over the indices of the array that point to a program. The iterator item type
     /// is `Result<u32, MapError>`.
     pub fn indices(&self) -> MapKeys<'_, u32> {
-        MapKeys::new(&self.inner)
-    }
-
-    fn check_bounds(&self, index: u32) -> Result<(), MapError> {
-        let max_entries = self.inner.obj.max_entries();
-        if index >= self.inner.obj.max_entries() {
-            Err(MapError::OutOfBounds { index, max_entries })
-        } else {
-            Ok(())
-        }
+        MapKeys::new(self.inner.as_ref())
     }
 }
 
-impl<T: Deref<Target = Map> + DerefMut<Target = Map>> ProgramArray<T> {
+impl<T: AsMut<MapData>> ProgramArray<T> {
     /// Sets the target program file descriptor for the given index in the jump table.
     ///
     /// When an eBPF program calls `bpf_tail_call(ctx, prog_array, index)`, control
     /// flow will jump to `program`.
     pub fn set(&mut self, index: u32, program: ProgramFd, flags: u64) -> Result<(), MapError> {
-        let fd = self.inner.fd_or_err()?;
-        self.check_bounds(index)?;
+        let data = self.inner.as_mut();
+        check_bounds(data, index)?;
+        let fd = data.fd_or_err()?;
         let prog_fd = program.as_raw_fd();
 
         bpf_map_update_elem(fd, Some(&index), &prog_fd, flags).map_err(|(_, io_error)| {
@@ -115,8 +93,10 @@ impl<T: Deref<Target = Map> + DerefMut<Target = Map>> ProgramArray<T> {
     /// Calling `bpf_tail_call(ctx, prog_array, index)` on an index that has been cleared returns an
     /// error.
     pub fn clear_index(&mut self, index: &u32) -> Result<(), MapError> {
-        let fd = self.inner.fd_or_err()?;
-        self.check_bounds(*index)?;
+        let data = self.inner.as_mut();
+        check_bounds(data, *index)?;
+        let fd = self.inner.as_mut().fd_or_err()?;
+
         bpf_map_delete_elem(fd, index)
             .map(|_| ())
             .map_err(|(_, io_error)| MapError::SyscallError {
@@ -125,19 +105,3 @@ impl<T: Deref<Target = Map> + DerefMut<Target = Map>> ProgramArray<T> {
             })
     }
 }
-
-impl TryFrom<MapRef> for ProgramArray<MapRef> {
-    type Error = MapError;
-
-    fn try_from(a: MapRef) -> Result<ProgramArray<MapRef>, MapError> {
-        ProgramArray::new(a)
-    }
-}
-
-impl TryFrom<MapRefMut> for ProgramArray<MapRefMut> {
-    type Error = MapError;
-
-    fn try_from(a: MapRefMut) -> Result<ProgramArray<MapRefMut>, MapError> {
-        ProgramArray::new(a)
-    }
-}
diff --git a/aya/src/maps/bloom_filter.rs b/aya/src/maps/bloom_filter.rs
index 03902ae65..5e94e247a 100644
--- a/aya/src/maps/bloom_filter.rs
+++ b/aya/src/maps/bloom_filter.rs
@@ -1,11 +1,8 @@
 //! A Bloom Filter.
-use std::{marker::PhantomData, ops::Deref};
-
-use core::mem;
+use std::{convert::AsRef, marker::PhantomData};
 
 use crate::{
-    generated::bpf_map_type::BPF_MAP_TYPE_BLOOM_FILTER,
-    maps::{Map, MapError, MapRef, MapRefMut},
+    maps::{check_v_size, MapData, MapError},
     sys::{bpf_map_lookup_elem_ptr, bpf_map_push_elem},
     Pod,
 };
@@ -19,10 +16,10 @@ use crate::{
 /// # Examples
 ///
 /// ```no_run
-/// # let bpf = aya::Bpf::load(&[])?;
+/// # let mut bpf = aya::Bpf::load(&[])?;
 /// use aya::maps::bloom_filter::BloomFilter;
 ///
-/// let mut bloom_filter = BloomFilter::try_from(bpf.map_mut("BLOOM_FILTER")?)?;
+/// let mut bloom_filter = BloomFilter::try_from(bpf.map_mut("BLOOM_FILTER").unwrap())?;
 ///
 /// bloom_filter.insert(1, 0)?;
 ///
@@ -33,27 +30,17 @@ use crate::{
 /// ```
 
 #[doc(alias = "BPF_MAP_TYPE_BLOOM_FILTER")]
-pub struct BloomFilter<T: Deref<Target = Map>, V: Pod> {
+pub struct BloomFilter<T, V: Pod> {
     inner: T,
     _v: PhantomData<V>,
 }
 
-impl<T: Deref<Target = Map>, V: Pod> BloomFilter<T, V> {
+impl<T: AsRef<MapData>, V: Pod> BloomFilter<T, V> {
     pub(crate) fn new(map: T) -> Result<BloomFilter<T, V>, MapError> {
-        let map_type = map.obj.map_type();
-
-        // validate the map definition
-        if map_type != BPF_MAP_TYPE_BLOOM_FILTER as u32 {
-            return Err(MapError::InvalidMapType { map_type });
-        }
-
-        let size = mem::size_of::<V>();
-        let expected = map.obj.value_size() as usize;
-        if size != expected {
-            return Err(MapError::InvalidValueSize { size, expected });
-        };
+        let data = map.as_ref();
+        check_v_size::<V>(data)?;
 
-        let _ = map.fd_or_err()?;
+        let _ = data.fd_or_err()?;
 
         Ok(BloomFilter {
             inner: map,
@@ -63,7 +50,7 @@ impl<T: Deref<Target = Map>, V: Pod> BloomFilter<T, V> {
 
     /// Query the existence of the element.
     pub fn contains(&self, mut value: &V, flags: u64) -> Result<(), MapError> {
-        let fd = self.inner.deref().fd_or_err()?;
+        let fd = self.inner.as_ref().fd_or_err()?;
 
         bpf_map_lookup_elem_ptr::<u32, _>(fd, None, &mut value, flags)
             .map_err(|(_, io_error)| MapError::SyscallError {
@@ -76,7 +63,7 @@ impl<T: Deref<Target = Map>, V: Pod> BloomFilter<T, V> {
 
     /// Inserts a value into the map.
     pub fn insert(&self, value: V, flags: u64) -> Result<(), MapError> {
-        let fd = self.inner.deref().fd_or_err()?;
+        let fd = self.inner.as_ref().fd_or_err()?;
         bpf_map_push_elem(fd, &value, flags).map_err(|(_, io_error)| MapError::SyscallError {
             call: "bpf_map_push_elem".to_owned(),
             io_error,
@@ -85,38 +72,6 @@ impl<T: Deref<Target = Map>, V: Pod> BloomFilter<T, V> {
     }
 }
 
-impl<V: Pod> TryFrom<MapRef> for BloomFilter<MapRef, V> {
-    type Error = MapError;
-
-    fn try_from(a: MapRef) -> Result<BloomFilter<MapRef, V>, MapError> {
-        BloomFilter::new(a)
-    }
-}
-
-impl<V: Pod> TryFrom<MapRefMut> for BloomFilter<MapRefMut, V> {
-    type Error = MapError;
-
-    fn try_from(a: MapRefMut) -> Result<BloomFilter<MapRefMut, V>, MapError> {
-        BloomFilter::new(a)
-    }
-}
-
-impl<'a, V: Pod> TryFrom<&'a Map> for BloomFilter<&'a Map, V> {
-    type Error = MapError;
-
-    fn try_from(a: &'a Map) -> Result<BloomFilter<&'a Map, V>, MapError> {
-        BloomFilter::new(a)
-    }
-}
-
-impl<'a, V: Pod> TryFrom<&'a mut Map> for BloomFilter<&'a mut Map, V> {
-    type Error = MapError;
-
-    fn try_from(a: &'a mut Map) -> Result<BloomFilter<&'a mut Map, V>, MapError> {
-        BloomFilter::new(a)
-    }
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -126,6 +81,7 @@ mod tests {
             bpf_cmd,
             bpf_map_type::{BPF_MAP_TYPE_BLOOM_FILTER, BPF_MAP_TYPE_PERF_EVENT_ARRAY},
         },
+        maps::{Map, MapData},
         obj,
         sys::{override_syscall, SysResult, Syscall},
     };
@@ -154,7 +110,7 @@ mod tests {
 
     #[test]
     fn test_wrong_value_size() {
-        let map = Map {
+        let map = MapData {
             obj: new_obj_map(),
             fd: None,
             pinned: false,
@@ -171,7 +127,7 @@ mod tests {
 
     #[test]
     fn test_try_from_wrong_map() {
-        let map = Map {
+        let map_data = MapData {
             obj: obj::Map::Legacy(obj::LegacyMap {
                 def: bpf_map_def {
                     map_type: BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32,
@@ -190,6 +146,8 @@ mod tests {
             btf_fd: None,
         };
 
+        let map = Map::PerfEventArray(map_data);
+
         assert!(matches!(
             BloomFilter::<_, u32>::try_from(&map),
             Err(MapError::InvalidMapType { .. })
@@ -198,7 +156,7 @@ mod tests {
 
     #[test]
     fn test_new_not_created() {
-        let mut map = Map {
+        let mut map = MapData {
             obj: new_obj_map(),
             fd: None,
             pinned: false,
@@ -213,7 +171,7 @@ mod tests {
 
     #[test]
     fn test_new_ok() {
-        let mut map = Map {
+        let mut map = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
@@ -225,12 +183,14 @@ mod tests {
 
     #[test]
     fn test_try_from_ok() {
-        let map = Map {
+        let map_data = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
             btf_fd: None,
         };
+
+        let map = Map::BloomFilter(map_data);
         assert!(BloomFilter::<_, u32>::try_from(&map).is_ok())
     }
 
@@ -238,7 +198,7 @@ mod tests {
     fn test_insert_syscall_error() {
         override_syscall(|_| sys_error(EFAULT));
 
-        let mut map = Map {
+        let mut map = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
@@ -262,7 +222,7 @@ mod tests {
             _ => sys_error(EFAULT),
         });
 
-        let mut map = Map {
+        let mut map = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
@@ -276,7 +236,7 @@ mod tests {
     #[test]
     fn test_contains_syscall_error() {
         override_syscall(|_| sys_error(EFAULT));
-        let map = Map {
+        let map = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
@@ -299,7 +259,7 @@ mod tests {
             } => sys_error(ENOENT),
             _ => sys_error(EFAULT),
         });
-        let map = Map {
+        let map = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
diff --git a/aya/src/maps/hash_map/hash_map.rs b/aya/src/maps/hash_map/hash_map.rs
index 6e36081c6..4c480ca2e 100644
--- a/aya/src/maps/hash_map/hash_map.rs
+++ b/aya/src/maps/hash_map/hash_map.rs
@@ -1,11 +1,10 @@
 use std::{
+    convert::{AsMut, AsRef},
     marker::PhantomData,
-    ops::{Deref, DerefMut},
 };
 
 use crate::{
-    generated::bpf_map_type::{BPF_MAP_TYPE_HASH, BPF_MAP_TYPE_LRU_HASH},
-    maps::{hash_map, IterableMap, Map, MapError, MapIter, MapKeys, MapRef, MapRefMut},
+    maps::{check_kv_size, hash_map, IterableMap, MapData, MapError, MapIter, MapKeys},
     sys::bpf_map_lookup_elem,
     Pod,
 };
@@ -19,10 +18,10 @@ use crate::{
 /// # Examples
 ///
 /// ```no_run
-/// # let bpf = aya::Bpf::load(&[])?;
+/// # let mut bpf = aya::Bpf::load(&[])?;
 /// use aya::maps::HashMap;
 ///
-/// let mut redirect_ports = HashMap::try_from(bpf.map_mut("REDIRECT_PORTS")?)?;
+/// let mut redirect_ports = HashMap::try_from(bpf.map_mut("REDIRECT_PORTS").unwrap())?;
 ///
 /// // redirect port 80 to 8080
 /// redirect_ports.insert(80, 8080, 0);
@@ -32,22 +31,18 @@ use crate::{
 /// ```
 #[doc(alias = "BPF_MAP_TYPE_HASH")]
 #[doc(alias = "BPF_MAP_TYPE_LRU_HASH")]
-pub struct HashMap<T: Deref<Target = Map>, K, V> {
+#[derive(Debug)]
+pub struct HashMap<T, K, V> {
     inner: T,
     _k: PhantomData<K>,
     _v: PhantomData<V>,
 }
 
-impl<T: Deref<Target = Map>, K: Pod, V: Pod> HashMap<T, K, V> {
+impl<T: AsRef<MapData>, K: Pod, V: Pod> HashMap<T, K, V> {
     pub(crate) fn new(map: T) -> Result<HashMap<T, K, V>, MapError> {
-        let map_type = map.obj.map_type();
-
-        // validate the map definition
-        if map_type != BPF_MAP_TYPE_HASH as u32 && map_type != BPF_MAP_TYPE_LRU_HASH as u32 {
-            return Err(MapError::InvalidMapType { map_type });
-        }
-        hash_map::check_kv_size::<K, V>(&map)?;
-        let _ = map.fd_or_err()?;
+        let data = map.as_ref();
+        check_kv_size::<K, V>(data)?;
+        let _ = data.fd_or_err()?;
 
         Ok(HashMap {
             inner: map,
@@ -58,7 +53,7 @@ impl<T: Deref<Target = Map>, K: Pod, V: Pod> HashMap<T, K, V> {
 
     /// Returns a copy of the value associated with the key.
     pub fn get(&self, key: &K, flags: u64) -> Result<V, MapError> {
-        let fd = self.inner.deref().fd_or_err()?;
+        let fd = self.inner.as_ref().fd_or_err()?;
         let value = bpf_map_lookup_elem(fd, key, flags).map_err(|(_, io_error)| {
             MapError::SyscallError {
                 call: "bpf_map_lookup_elem".to_owned(),
@@ -77,25 +72,25 @@ impl<T: Deref<Target = Map>, K: Pod, V: Pod> HashMap<T, K, V> {
     /// An iterator visiting all keys in arbitrary order. The iterator element
     /// type is `Result<K, MapError>`.
     pub fn keys(&self) -> MapKeys<'_, K> {
-        MapKeys::new(&self.inner)
+        MapKeys::new(self.inner.as_ref())
     }
 }
 
-impl<T: DerefMut<Target = Map>, K: Pod, V: Pod> HashMap<T, K, V> {
+impl<T: AsMut<MapData>, K: Pod, V: Pod> HashMap<T, K, V> {
     /// Inserts a key-value pair into the map.
     pub fn insert(&mut self, key: K, value: V, flags: u64) -> Result<(), MapError> {
-        hash_map::insert(&mut self.inner, key, value, flags)
+        hash_map::insert(self.inner.as_mut(), key, value, flags)
     }
 
     /// Removes a key from the map.
     pub fn remove(&mut self, key: &K) -> Result<(), MapError> {
-        hash_map::remove(&mut self.inner, key)
+        hash_map::remove(self.inner.as_mut(), key)
     }
 }
 
-impl<T: Deref<Target = Map>, K: Pod, V: Pod> IterableMap<K, V> for HashMap<T, K, V> {
-    fn map(&self) -> &Map {
-        &self.inner
+impl<T: AsRef<MapData>, K: Pod, V: Pod> IterableMap<K, V> for HashMap<T, K, V> {
+    fn map(&self) -> &MapData {
+        self.inner.as_ref()
     }
 
     fn get(&self, key: &K) -> Result<V, MapError> {
@@ -103,38 +98,6 @@ impl<T: Deref<Target = Map>, K: Pod, V: Pod> IterableMap<K, V> for HashMap<T, K,
     }
 }
 
-impl<K: Pod, V: Pod> TryFrom<MapRef> for HashMap<MapRef, K, V> {
-    type Error = MapError;
-
-    fn try_from(a: MapRef) -> Result<HashMap<MapRef, K, V>, MapError> {
-        HashMap::new(a)
-    }
-}
-
-impl<K: Pod, V: Pod> TryFrom<MapRefMut> for HashMap<MapRefMut, K, V> {
-    type Error = MapError;
-
-    fn try_from(a: MapRefMut) -> Result<HashMap<MapRefMut, K, V>, MapError> {
-        HashMap::new(a)
-    }
-}
-
-impl<'a, K: Pod, V: Pod> TryFrom<&'a Map> for HashMap<&'a Map, K, V> {
-    type Error = MapError;
-
-    fn try_from(a: &'a Map) -> Result<HashMap<&'a Map, K, V>, MapError> {
-        HashMap::new(a)
-    }
-}
-
-impl<'a, K: Pod, V: Pod> TryFrom<&'a mut Map> for HashMap<&'a mut Map, K, V> {
-    type Error = MapError;
-
-    fn try_from(a: &'a mut Map) -> Result<HashMap<&'a mut Map, K, V>, MapError> {
-        HashMap::new(a)
-    }
-}
-
 #[cfg(test)]
 mod tests {
     use std::io;
@@ -145,8 +108,9 @@ mod tests {
         bpf_map_def,
         generated::{
             bpf_attr, bpf_cmd,
-            bpf_map_type::{BPF_MAP_TYPE_HASH, BPF_MAP_TYPE_PERF_EVENT_ARRAY},
+            bpf_map_type::{BPF_MAP_TYPE_HASH, BPF_MAP_TYPE_LRU_HASH},
         },
+        maps::{Map, MapData},
         obj,
         sys::{override_syscall, SysResult, Syscall},
     };
@@ -175,7 +139,7 @@ mod tests {
 
     #[test]
     fn test_wrong_key_size() {
-        let map = Map {
+        let map = MapData {
             obj: new_obj_map(),
             fd: None,
             pinned: false,
@@ -192,7 +156,7 @@ mod tests {
 
     #[test]
     fn test_wrong_value_size() {
-        let map = Map {
+        let map = MapData {
             obj: new_obj_map(),
             fd: None,
             pinned: false,
@@ -209,34 +173,42 @@ mod tests {
 
     #[test]
     fn test_try_from_wrong_map() {
-        let map = Map {
-            obj: obj::Map::Legacy(obj::LegacyMap {
-                def: bpf_map_def {
-                    map_type: BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32,
-                    key_size: 4,
-                    value_size: 4,
-                    max_entries: 1024,
-                    ..Default::default()
-                },
-                section_index: 0,
-                symbol_index: 0,
-                data: Vec::new(),
-                kind: obj::MapKind::Other,
-            }),
+        let map_data = MapData {
+            obj: new_obj_map(),
             fd: None,
             pinned: false,
             btf_fd: None,
         };
 
+        let map = Map::Array(map_data);
         assert!(matches!(
-            HashMap::<_, u32, u32>::try_from(&map),
+            HashMap::<_, u8, u32>::try_from(&map),
             Err(MapError::InvalidMapType { .. })
         ));
     }
 
+    #[test]
+    fn test_try_from_wrong_map_values() {
+        let map_data = MapData {
+            obj: new_obj_map(),
+            fd: None,
+            pinned: false,
+            btf_fd: None,
+        };
+
+        let map = Map::HashMap(map_data);
+        assert!(matches!(
+            HashMap::<_, u32, u16>::try_from(&map),
+            Err(MapError::InvalidValueSize {
+                size: 2,
+                expected: 4
+            })
+        ));
+    }
+
     #[test]
     fn test_new_not_created() {
-        let mut map = Map {
+        let mut map = MapData {
             obj: new_obj_map(),
             fd: None,
             pinned: false,
@@ -251,7 +223,7 @@ mod tests {
 
     #[test]
     fn test_new_ok() {
-        let mut map = Map {
+        let mut map = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
@@ -263,18 +235,20 @@ mod tests {
 
     #[test]
     fn test_try_from_ok() {
-        let map = Map {
+        let map_data = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
             btf_fd: None,
         };
+
+        let map = Map::HashMap(map_data);
         assert!(HashMap::<_, u32, u32>::try_from(&map).is_ok())
     }
 
     #[test]
     fn test_try_from_ok_lru() {
-        let map = Map {
+        let map_data = MapData {
             obj: obj::Map::Legacy(obj::LegacyMap {
                 def: bpf_map_def {
                     map_type: BPF_MAP_TYPE_LRU_HASH as u32,
@@ -293,6 +267,8 @@ mod tests {
             btf_fd: None,
         };
 
+        let map = Map::HashMap(map_data);
+
         assert!(HashMap::<_, u32, u32>::try_from(&map).is_ok())
     }
 
@@ -300,7 +276,7 @@ mod tests {
     fn test_insert_syscall_error() {
         override_syscall(|_| sys_error(EFAULT));
 
-        let mut map = Map {
+        let mut map = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
@@ -324,7 +300,7 @@ mod tests {
             _ => sys_error(EFAULT),
         });
 
-        let mut map = Map {
+        let mut map = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
@@ -339,7 +315,7 @@ mod tests {
     fn test_remove_syscall_error() {
         override_syscall(|_| sys_error(EFAULT));
 
-        let mut map = Map {
+        let mut map = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
@@ -363,7 +339,7 @@ mod tests {
             _ => sys_error(EFAULT),
         });
 
-        let mut map = Map {
+        let mut map = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
@@ -377,7 +353,7 @@ mod tests {
     #[test]
     fn test_get_syscall_error() {
         override_syscall(|_| sys_error(EFAULT));
-        let map = Map {
+        let map = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
@@ -400,7 +376,7 @@ mod tests {
             } => sys_error(ENOENT),
             _ => sys_error(EFAULT),
         });
-        let map = Map {
+        let map = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
@@ -437,7 +413,7 @@ mod tests {
             } => sys_error(ENOENT),
             _ => sys_error(EFAULT),
         });
-        let map = Map {
+        let map = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
@@ -486,7 +462,7 @@ mod tests {
             _ => sys_error(EFAULT),
         });
 
-        let map = Map {
+        let map = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
@@ -519,7 +495,7 @@ mod tests {
             }
             _ => sys_error(EFAULT),
         });
-        let map = Map {
+        let map = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
@@ -554,7 +530,7 @@ mod tests {
             } => lookup_elem(attr),
             _ => sys_error(EFAULT),
         });
-        let map = Map {
+        let map = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
@@ -592,7 +568,7 @@ mod tests {
             }
             _ => sys_error(EFAULT),
         });
-        let map = Map {
+        let map = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
@@ -631,7 +607,7 @@ mod tests {
             } => lookup_elem(attr),
             _ => sys_error(EFAULT),
         });
-        let map = Map {
+        let map = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
@@ -676,7 +652,7 @@ mod tests {
             }
             _ => sys_error(EFAULT),
         });
-        let map = Map {
+        let map = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
diff --git a/aya/src/maps/hash_map/mod.rs b/aya/src/maps/hash_map/mod.rs
index 60d0ec8a7..5be375c9c 100644
--- a/aya/src/maps/hash_map/mod.rs
+++ b/aya/src/maps/hash_map/mod.rs
@@ -1,8 +1,6 @@
 //! Hash map types.
-use std::mem;
-
 use crate::{
-    maps::{Map, MapError},
+    maps::MapError,
     sys::{bpf_map_delete_elem, bpf_map_update_elem},
 };
 
@@ -13,21 +11,14 @@ mod per_cpu_hash_map;
 pub use hash_map::*;
 pub use per_cpu_hash_map::*;
 
-pub(crate) fn check_kv_size<K, V>(map: &Map) -> Result<(), MapError> {
-    let size = mem::size_of::<K>();
-    let expected = map.obj.key_size() as usize;
-    if size != expected {
-        return Err(MapError::InvalidKeySize { size, expected });
-    }
-    let size = mem::size_of::<V>();
-    let expected = map.obj.value_size() as usize;
-    if size != expected {
-        return Err(MapError::InvalidValueSize { size, expected });
-    };
-    Ok(())
-}
+use super::MapData;
 
-pub(crate) fn insert<K, V>(map: &mut Map, key: K, value: V, flags: u64) -> Result<(), MapError> {
+pub(crate) fn insert<K, V>(
+    map: &mut MapData,
+    key: K,
+    value: V,
+    flags: u64,
+) -> Result<(), MapError> {
     let fd = map.fd_or_err()?;
     bpf_map_update_elem(fd, Some(&key), &value, flags).map_err(|(_, io_error)| {
         MapError::SyscallError {
@@ -39,7 +30,7 @@ pub(crate) fn insert<K, V>(map: &mut Map, key: K, value: V, flags: u64) -> Resul
     Ok(())
 }
 
-pub(crate) fn remove<K>(map: &mut Map, key: &K) -> Result<(), MapError> {
+pub(crate) fn remove<K>(map: &mut MapData, key: &K) -> Result<(), MapError> {
     let fd = map.fd_or_err()?;
     bpf_map_delete_elem(fd, key)
         .map(|_| ())
diff --git a/aya/src/maps/hash_map/per_cpu_hash_map.rs b/aya/src/maps/hash_map/per_cpu_hash_map.rs
index a539965d2..0f38f6c63 100644
--- a/aya/src/maps/hash_map/per_cpu_hash_map.rs
+++ b/aya/src/maps/hash_map/per_cpu_hash_map.rs
@@ -1,13 +1,12 @@
 //! Per-CPU hash map.
 use std::{
+    convert::{AsMut, AsRef},
     marker::PhantomData,
-    ops::{Deref, DerefMut},
 };
 
 use crate::{
-    generated::bpf_map_type::{BPF_MAP_TYPE_LRU_PERCPU_HASH, BPF_MAP_TYPE_PERCPU_HASH},
     maps::{
-        hash_map, IterableMap, Map, MapError, MapIter, MapKeys, MapRef, MapRefMut, PerCpuValues,
+        check_kv_size, hash_map, IterableMap, MapData, MapError, MapIter, MapKeys, PerCpuValues,
     },
     sys::{bpf_map_lookup_elem_per_cpu, bpf_map_update_elem_per_cpu},
     Pod,
@@ -26,13 +25,13 @@ use crate::{
 /// # Examples
 ///
 /// ```no_run
-/// # let bpf = aya::Bpf::load(&[])?;
+/// # let mut bpf = aya::Bpf::load(&[])?;
 /// use aya::maps::PerCpuHashMap;
 ///
 /// const CPU_IDS: u8 = 1;
 /// const WAKEUPS: u8 = 2;
 ///
-/// let mut hm = PerCpuHashMap::<_, u8, u32>::try_from(bpf.map("COUNTERS")?)?;
+/// let mut hm = PerCpuHashMap::<_, u8, u32>::try_from(bpf.map_mut("PER_CPU_STORAGE").unwrap())?;
 /// let cpu_ids = unsafe { hm.get(&CPU_IDS, 0)? };
 /// let wakeups = unsafe { hm.get(&WAKEUPS, 0)? };
 /// for (cpu_id, wakeups) in cpu_ids.iter().zip(wakeups.iter()) {
@@ -42,24 +41,18 @@ use crate::{
 /// ```
 #[doc(alias = "BPF_MAP_TYPE_LRU_PERCPU_HASH")]
 #[doc(alias = "BPF_MAP_TYPE_PERCPU_HASH")]
-pub struct PerCpuHashMap<T: Deref<Target = Map>, K: Pod, V: Pod> {
+pub struct PerCpuHashMap<T, K: Pod, V: Pod> {
     inner: T,
     _k: PhantomData<K>,
     _v: PhantomData<V>,
 }
 
-impl<T: Deref<Target = Map>, K: Pod, V: Pod> PerCpuHashMap<T, K, V> {
+impl<T: AsRef<MapData>, K: Pod, V: Pod> PerCpuHashMap<T, K, V> {
     pub(crate) fn new(map: T) -> Result<PerCpuHashMap<T, K, V>, MapError> {
-        let map_type = map.obj.map_type();
+        let data = map.as_ref();
+        check_kv_size::<K, V>(data)?;
 
-        // validate the map definition
-        if map_type != BPF_MAP_TYPE_PERCPU_HASH as u32
-            && map_type != BPF_MAP_TYPE_LRU_PERCPU_HASH as u32
-        {
-            return Err(MapError::InvalidMapType { map_type });
-        }
-        hash_map::check_kv_size::<K, V>(&map)?;
-        let _ = map.fd_or_err()?;
+        let _ = data.fd_or_err()?;
 
         Ok(PerCpuHashMap {
             inner: map,
@@ -70,7 +63,7 @@ impl<T: Deref<Target = Map>, K: Pod, V: Pod> PerCpuHashMap<T, K, V> {
 
     /// Returns a slice of values - one for each CPU - associated with the key.
     pub fn get(&self, key: &K, flags: u64) -> Result<PerCpuValues<V>, MapError> {
-        let fd = self.inner.deref().fd_or_err()?;
+        let fd = self.inner.as_ref().fd_or_err()?;
         let values = bpf_map_lookup_elem_per_cpu(fd, key, flags).map_err(|(_, io_error)| {
             MapError::SyscallError {
                 call: "bpf_map_lookup_elem".to_owned(),
@@ -89,11 +82,11 @@ impl<T: Deref<Target = Map>, K: Pod, V: Pod> PerCpuHashMap<T, K, V> {
     /// An iterator visiting all keys in arbitrary order. The iterator element
     /// type is `Result<K, MapError>`.
     pub fn keys(&self) -> MapKeys<'_, K> {
-        MapKeys::new(&self.inner)
+        MapKeys::new(self.inner.as_ref())
     }
 }
 
-impl<T: DerefMut<Target = Map>, K: Pod, V: Pod> PerCpuHashMap<T, K, V> {
+impl<T: AsMut<MapData>, K: Pod, V: Pod> PerCpuHashMap<T, K, V> {
     /// Inserts a slice of values - one for each CPU - for the given key.
     ///
     /// # Examples
@@ -108,13 +101,13 @@ impl<T: DerefMut<Target = Map>, K: Pod, V: Pod> PerCpuHashMap<T, K, V> {
     /// #     #[error(transparent)]
     /// #     Bpf(#[from] aya::BpfError)
     /// # }
-    /// # let bpf = aya::Bpf::load(&[])?;
+    /// # let mut bpf = aya::Bpf::load(&[])?;
     /// use aya::maps::{PerCpuHashMap, PerCpuValues};
     /// use aya::util::nr_cpus;
     ///
     /// const RETRIES: u8 = 1;
     ///
-    /// let mut hm = PerCpuHashMap::<_, u8, u32>::try_from(bpf.map_mut("PER_CPU_STORAGE")?)?;
+    /// let mut hm = PerCpuHashMap::<_, u8, u32>::try_from(bpf.map_mut("PER_CPU_STORAGE").unwrap())?;
     /// hm.insert(
     ///     RETRIES,
     ///     PerCpuValues::try_from(vec![3u32; nr_cpus()?])?,
@@ -123,7 +116,7 @@ impl<T: DerefMut<Target = Map>, K: Pod, V: Pod> PerCpuHashMap<T, K, V> {
     /// # Ok::<(), Error>(())
     /// ```
     pub fn insert(&mut self, key: K, values: PerCpuValues<V>, flags: u64) -> Result<(), MapError> {
-        let fd = self.inner.fd_or_err()?;
+        let fd = self.inner.as_mut().fd_or_err()?;
         bpf_map_update_elem_per_cpu(fd, &key, &values, flags).map_err(|(_, io_error)| {
             MapError::SyscallError {
                 call: "bpf_map_update_elem".to_owned(),
@@ -136,50 +129,16 @@ impl<T: DerefMut<Target = Map>, K: Pod, V: Pod> PerCpuHashMap<T, K, V> {
 
     /// Removes a key from the map.
     pub fn remove(&mut self, key: &K) -> Result<(), MapError> {
-        hash_map::remove(&mut self.inner, key)
+        hash_map::remove(self.inner.as_mut(), key)
     }
 }
 
-impl<T: Deref<Target = Map>, K: Pod, V: Pod> IterableMap<K, PerCpuValues<V>>
-    for PerCpuHashMap<T, K, V>
-{
-    fn map(&self) -> &Map {
-        &self.inner
+impl<T: AsRef<MapData>, K: Pod, V: Pod> IterableMap<K, PerCpuValues<V>> for PerCpuHashMap<T, K, V> {
+    fn map(&self) -> &MapData {
+        self.inner.as_ref()
     }
 
     fn get(&self, key: &K) -> Result<PerCpuValues<V>, MapError> {
         PerCpuHashMap::get(self, key, 0)
     }
 }
-
-impl<K: Pod, V: Pod> TryFrom<MapRef> for PerCpuHashMap<MapRef, K, V> {
-    type Error = MapError;
-
-    fn try_from(a: MapRef) -> Result<PerCpuHashMap<MapRef, K, V>, MapError> {
-        PerCpuHashMap::new(a)
-    }
-}
-
-impl<K: Pod, V: Pod> TryFrom<MapRefMut> for PerCpuHashMap<MapRefMut, K, V> {
-    type Error = MapError;
-
-    fn try_from(a: MapRefMut) -> Result<PerCpuHashMap<MapRefMut, K, V>, MapError> {
-        PerCpuHashMap::new(a)
-    }
-}
-
-impl<'a, K: Pod, V: Pod> TryFrom<&'a Map> for PerCpuHashMap<&'a Map, K, V> {
-    type Error = MapError;
-
-    fn try_from(a: &'a Map) -> Result<PerCpuHashMap<&'a Map, K, V>, MapError> {
-        PerCpuHashMap::new(a)
-    }
-}
-
-impl<'a, K: Pod, V: Pod> TryFrom<&'a mut Map> for PerCpuHashMap<&'a mut Map, K, V> {
-    type Error = MapError;
-
-    fn try_from(a: &'a mut Map) -> Result<PerCpuHashMap<&'a mut Map, K, V>, MapError> {
-        PerCpuHashMap::new(a)
-    }
-}
diff --git a/aya/src/maps/lpm_trie.rs b/aya/src/maps/lpm_trie.rs
index 989ee51f6..ea6d8ffcb 100644
--- a/aya/src/maps/lpm_trie.rs
+++ b/aya/src/maps/lpm_trie.rs
@@ -1,9 +1,12 @@
 //! A LPM Trie.
-use std::{marker::PhantomData, mem, ops::Deref};
+use std::{
+    convert::{AsMut, AsRef},
+    marker::PhantomData,
+    mem,
+};
 
 use crate::{
-    generated::bpf_map_type::BPF_MAP_TYPE_LPM_TRIE,
-    maps::{IterableMap, Map, MapError, MapRef, MapRefMut},
+    maps::{check_kv_size, IterableMap, MapData, MapError},
     sys::{bpf_map_delete_elem, bpf_map_lookup_elem, bpf_map_update_elem},
     Pod,
 };
@@ -17,11 +20,11 @@ use crate::{
 /// # Examples
 ///
 /// ```no_run
-/// # let bpf = aya::Bpf::load(&[])?;
+/// # let mut bpf = aya::Bpf::load(&[])?;
 /// use aya::maps::lpm_trie::{LpmTrie, Key};
 /// use std::net::Ipv4Addr;
 ///
-/// let mut trie = LpmTrie::try_from(bpf.map_mut("LPM_TRIE")?)?;
+/// let mut trie = LpmTrie::try_from(bpf.map_mut("LPM_TRIE").unwrap())?;
 /// let ipaddr = Ipv4Addr::new(8, 8, 8, 8);
 /// // The following represents a key for the "8.8.8.8/16" subnet.
 /// // The first argument - the prefix length - represents how many bytes should be matched against. The second argument is the actual data to be matched.
@@ -43,7 +46,7 @@ use crate::{
 /// ```
 
 #[doc(alias = "BPF_MAP_TYPE_LPM_TRIE")]
-pub struct LpmTrie<T: Deref<Target = Map>, K, V> {
+pub struct LpmTrie<T, K, V> {
     inner: T,
     _k: PhantomData<K>,
     _v: PhantomData<V>,
@@ -96,26 +99,12 @@ impl<K: Pod> Clone for Key<K> {
 // A Pod impl is required as Key struct is a key for a map.
 unsafe impl<K: Pod> Pod for Key<K> {}
 
-impl<T: Deref<Target = Map>, K: Pod, V: Pod> LpmTrie<T, K, V> {
+impl<T: AsRef<MapData>, K: Pod, V: Pod> LpmTrie<T, K, V> {
     pub(crate) fn new(map: T) -> Result<LpmTrie<T, K, V>, MapError> {
-        let map_type = map.obj.map_type();
-
-        // validate the map definition
-        if map_type != BPF_MAP_TYPE_LPM_TRIE as u32 {
-            return Err(MapError::InvalidMapType { map_type });
-        }
-        let size = mem::size_of::<Key<K>>();
-        let expected = map.obj.key_size() as usize;
-        if size != expected {
-            return Err(MapError::InvalidKeySize { size, expected });
-        }
-        let size = mem::size_of::<V>();
-        let expected = map.obj.value_size() as usize;
-        if size != expected {
-            return Err(MapError::InvalidValueSize { size, expected });
-        };
+        let data = map.as_ref();
+        check_kv_size::<Key<K>, V>(data)?;
 
-        let _ = map.fd_or_err()?;
+        let _ = data.fd_or_err()?;
 
         Ok(LpmTrie {
             inner: map,
@@ -126,7 +115,7 @@ impl<T: Deref<Target = Map>, K: Pod, V: Pod> LpmTrie<T, K, V> {
 
     /// Returns a copy of the value associated with the longest prefix matching key in the LpmTrie.
     pub fn get(&self, key: &Key<K>, flags: u64) -> Result<V, MapError> {
-        let fd = self.inner.deref().fd_or_err()?;
+        let fd = self.inner.as_ref().fd_or_err()?;
         let value = bpf_map_lookup_elem(fd, key, flags).map_err(|(_, io_error)| {
             MapError::SyscallError {
                 call: "bpf_map_lookup_elem".to_owned(),
@@ -135,10 +124,12 @@ impl<T: Deref<Target = Map>, K: Pod, V: Pod> LpmTrie<T, K, V> {
         })?;
         value.ok_or(MapError::KeyNotFound)
     }
+}
 
+impl<T: AsMut<MapData>, K: Pod, V: Pod> LpmTrie<T, K, V> {
     /// Inserts a key value pair into the map.
-    pub fn insert(&self, key: &Key<K>, value: V, flags: u64) -> Result<(), MapError> {
-        let fd = self.inner.deref().fd_or_err()?;
+    pub fn insert(&mut self, key: &Key<K>, value: V, flags: u64) -> Result<(), MapError> {
+        let fd = self.inner.as_mut().fd_or_err()?;
         bpf_map_update_elem(fd, Some(key), &value, flags).map_err(|(_, io_error)| {
             MapError::SyscallError {
                 call: "bpf_map_update_elem".to_owned(),
@@ -152,8 +143,8 @@ impl<T: Deref<Target = Map>, K: Pod, V: Pod> LpmTrie<T, K, V> {
     /// Removes an element from the map.
     ///
     /// Both the prefix and data must match exactly - this method does not do a longest prefix match.
-    pub fn remove(&self, key: &Key<K>) -> Result<(), MapError> {
-        let fd = self.inner.deref().fd_or_err()?;
+    pub fn remove(&mut self, key: &Key<K>) -> Result<(), MapError> {
+        let fd = self.inner.as_mut().fd_or_err()?;
         bpf_map_delete_elem(fd, key)
             .map(|_| ())
             .map_err(|(_, io_error)| MapError::SyscallError {
@@ -163,9 +154,9 @@ impl<T: Deref<Target = Map>, K: Pod, V: Pod> LpmTrie<T, K, V> {
     }
 }
 
-impl<T: Deref<Target = Map>, K: Pod, V: Pod> IterableMap<K, V> for LpmTrie<T, K, V> {
-    fn map(&self) -> &Map {
-        &self.inner
+impl<T: AsRef<MapData>, K: Pod, V: Pod> IterableMap<K, V> for LpmTrie<T, K, V> {
+    fn map(&self) -> &MapData {
+        self.inner.as_ref()
     }
 
     fn get(&self, key: &K) -> Result<V, MapError> {
@@ -174,38 +165,6 @@ impl<T: Deref<Target = Map>, K: Pod, V: Pod> IterableMap<K, V> for LpmTrie<T, K,
     }
 }
 
-impl<K: Pod, V: Pod> TryFrom<MapRef> for LpmTrie<MapRef, K, V> {
-    type Error = MapError;
-
-    fn try_from(a: MapRef) -> Result<LpmTrie<MapRef, K, V>, MapError> {
-        LpmTrie::new(a)
-    }
-}
-
-impl<K: Pod, V: Pod> TryFrom<MapRefMut> for LpmTrie<MapRefMut, K, V> {
-    type Error = MapError;
-
-    fn try_from(a: MapRefMut) -> Result<LpmTrie<MapRefMut, K, V>, MapError> {
-        LpmTrie::new(a)
-    }
-}
-
-impl<'a, K: Pod, V: Pod> TryFrom<&'a Map> for LpmTrie<&'a Map, K, V> {
-    type Error = MapError;
-
-    fn try_from(a: &'a Map) -> Result<LpmTrie<&'a Map, K, V>, MapError> {
-        LpmTrie::new(a)
-    }
-}
-
-impl<'a, K: Pod, V: Pod> TryFrom<&'a mut Map> for LpmTrie<&'a mut Map, K, V> {
-    type Error = MapError;
-
-    fn try_from(a: &'a mut Map) -> Result<LpmTrie<&'a mut Map, K, V>, MapError> {
-        LpmTrie::new(a)
-    }
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -215,6 +174,7 @@ mod tests {
             bpf_cmd,
             bpf_map_type::{BPF_MAP_TYPE_LPM_TRIE, BPF_MAP_TYPE_PERF_EVENT_ARRAY},
         },
+        maps::{Map, MapData},
         obj,
         sys::{override_syscall, SysResult, Syscall},
     };
@@ -243,7 +203,7 @@ mod tests {
 
     #[test]
     fn test_wrong_key_size() {
-        let map = Map {
+        let map = MapData {
             obj: new_obj_map(),
             fd: None,
             pinned: false,
@@ -260,7 +220,7 @@ mod tests {
 
     #[test]
     fn test_wrong_value_size() {
-        let map = Map {
+        let map = MapData {
             obj: new_obj_map(),
             fd: None,
             pinned: false,
@@ -277,7 +237,7 @@ mod tests {
 
     #[test]
     fn test_try_from_wrong_map() {
-        let map = Map {
+        let map_data = MapData {
             obj: obj::Map::Legacy(obj::LegacyMap {
                 def: bpf_map_def {
                     map_type: BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32,
@@ -296,6 +256,8 @@ mod tests {
             pinned: false,
         };
 
+        let map = Map::PerfEventArray(map_data);
+
         assert!(matches!(
             LpmTrie::<_, u32, u32>::try_from(&map),
             Err(MapError::InvalidMapType { .. })
@@ -304,7 +266,7 @@ mod tests {
 
     #[test]
     fn test_new_not_created() {
-        let mut map = Map {
+        let mut map = MapData {
             obj: new_obj_map(),
             fd: None,
             pinned: false,
@@ -319,7 +281,7 @@ mod tests {
 
     #[test]
     fn test_new_ok() {
-        let mut map = Map {
+        let mut map = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
@@ -331,12 +293,14 @@ mod tests {
 
     #[test]
     fn test_try_from_ok() {
-        let map = Map {
+        let map_data = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
             btf_fd: None,
         };
+
+        let map = Map::LpmTrie(map_data);
         assert!(LpmTrie::<_, u32, u32>::try_from(&map).is_ok())
     }
 
@@ -344,13 +308,13 @@ mod tests {
     fn test_insert_syscall_error() {
         override_syscall(|_| sys_error(EFAULT));
 
-        let mut map = Map {
+        let mut map = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
             btf_fd: None,
         };
-        let trie = LpmTrie::<_, u32, u32>::new(&mut map).unwrap();
+        let mut trie = LpmTrie::<_, u32, u32>::new(&mut map).unwrap();
         let ipaddr = Ipv4Addr::new(8, 8, 8, 8);
         let key = Key::new(16, u32::from(ipaddr).to_be());
         assert!(matches!(
@@ -369,14 +333,14 @@ mod tests {
             _ => sys_error(EFAULT),
         });
 
-        let mut map = Map {
+        let mut map = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
             btf_fd: None,
         };
 
-        let trie = LpmTrie::<_, u32, u32>::new(&mut map).unwrap();
+        let mut trie = LpmTrie::<_, u32, u32>::new(&mut map).unwrap();
         let ipaddr = Ipv4Addr::new(8, 8, 8, 8);
         let key = Key::new(16, u32::from(ipaddr).to_be());
         assert!(trie.insert(&key, 1, 0).is_ok());
@@ -386,13 +350,13 @@ mod tests {
     fn test_remove_syscall_error() {
         override_syscall(|_| sys_error(EFAULT));
 
-        let mut map = Map {
+        let mut map = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
             btf_fd: None,
         };
-        let trie = LpmTrie::<_, u32, u32>::new(&mut map).unwrap();
+        let mut trie = LpmTrie::<_, u32, u32>::new(&mut map).unwrap();
         let ipaddr = Ipv4Addr::new(8, 8, 8, 8);
         let key = Key::new(16, u32::from(ipaddr).to_be());
         assert!(matches!(
@@ -411,13 +375,13 @@ mod tests {
             _ => sys_error(EFAULT),
         });
 
-        let mut map = Map {
+        let mut map = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
             btf_fd: None,
         };
-        let trie = LpmTrie::<_, u32, u32>::new(&mut map).unwrap();
+        let mut trie = LpmTrie::<_, u32, u32>::new(&mut map).unwrap();
         let ipaddr = Ipv4Addr::new(8, 8, 8, 8);
         let key = Key::new(16, u32::from(ipaddr).to_be());
         assert!(trie.remove(&key).is_ok());
@@ -426,7 +390,7 @@ mod tests {
     #[test]
     fn test_get_syscall_error() {
         override_syscall(|_| sys_error(EFAULT));
-        let map = Map {
+        let map = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
@@ -451,7 +415,7 @@ mod tests {
             } => sys_error(ENOENT),
             _ => sys_error(EFAULT),
         });
-        let map = Map {
+        let map = MapData {
             obj: new_obj_map(),
             fd: Some(42),
             pinned: false,
diff --git a/aya/src/maps/map_lock.rs b/aya/src/maps/map_lock.rs
deleted file mode 100644
index 18f87acfa..000000000
--- a/aya/src/maps/map_lock.rs
+++ /dev/null
@@ -1,81 +0,0 @@
-use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
-use std::{
-    mem,
-    ops::{Deref, DerefMut},
-    sync::Arc,
-};
-
-use crate::maps::Map;
-
-pub(crate) struct MapLockError;
-
-/* FIXME: write a full RwLock implementation that doesn't use borrowing guards
- * so that try_read() and try_write() don't have to use the ugly lifetime
- * extension hack */
-
-#[derive(Debug)]
-pub(crate) struct MapLock {
-    inner: Arc<RwLock<Map>>,
-}
-
-impl MapLock {
-    pub(crate) fn new(map: Map) -> MapLock {
-        MapLock {
-            inner: Arc::new(RwLock::new(map)),
-        }
-    }
-
-    pub(crate) fn try_read(&self) -> Result<MapRef, MapLockError> {
-        let lock: Option<RwLockReadGuard<'static, Map>> =
-            unsafe { mem::transmute(self.inner.try_read()) };
-        lock.map(|guard| MapRef {
-            _lock: self.inner.clone(),
-            guard,
-        })
-        .ok_or(MapLockError)
-    }
-
-    pub(crate) fn try_write(&self) -> Result<MapRefMut, MapLockError> {
-        let lock: Option<RwLockWriteGuard<'static, Map>> =
-            unsafe { mem::transmute(self.inner.try_write()) };
-        lock.map(|guard| MapRefMut {
-            _lock: self.inner.clone(),
-            guard,
-        })
-        .ok_or(MapLockError)
-    }
-}
-
-/// A borrowed reference to a BPF map.
-pub struct MapRef {
-    _lock: Arc<RwLock<Map>>,
-    guard: RwLockReadGuard<'static, Map>,
-}
-
-/// A mutable borrowed reference to a BPF map.
-pub struct MapRefMut {
-    _lock: Arc<RwLock<Map>>,
-    guard: RwLockWriteGuard<'static, Map>,
-}
-
-impl Deref for MapRef {
-    type Target = Map;
-
-    fn deref(&self) -> &Map {
-        &self.guard
-    }
-}
-
-impl Deref for MapRefMut {
-    type Target = Map;
-
-    fn deref(&self) -> &Map {
-        &self.guard
-    }
-}
-
-impl DerefMut for MapRefMut {
-    fn deref_mut(&mut self) -> &mut Map {
-        &mut self.guard
-    }
-}
diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs
index 9d8a13d19..e0b980be5 100644
--- a/aya/src/maps/mod.rs
+++ b/aya/src/maps/mod.rs
@@ -4,25 +4,30 @@
 //! used to setup and share data with eBPF programs. When you call
 //! [`Bpf::load_file`](crate::Bpf::load_file) or
 //! [`Bpf::load`](crate::Bpf::load), all the maps defined in the eBPF code get
-//! initialized and can then be accessed using [`Bpf::map`](crate::Bpf::map) and
-//! [`Bpf::map_mut`](crate::Bpf::map_mut).
+//! initialized and can then be accessed using [`Bpf::map`](crate::Bpf::map),
+//! [`Bpf::map_mut`](crate::Bpf::map_mut), or [`Bpf::take_map`](crate::Bpf::take_map).
 //!
 //! # Typed maps
 //!
 //! The eBPF API includes many map types each supporting different operations.
-//! [`Bpf::map`](crate::Bpf::map) and [`Bpf::map_mut`](crate::Bpf::map_mut) always return the
-//! opaque [`MapRef`] and [`MapRefMut`] types respectively. Those two types can be converted to
-//! *typed maps* using the [`TryFrom`](std::convert::TryFrom) trait. For example:
+//! [`Bpf::map`](crate::Bpf::map), [`Bpf::map_mut`](crate::Bpf::map_mut), and
+//! [`Bpf::take_map`](crate::Bpf::take_map) always return the
+//! opaque [`&Map`](crate::maps::Map), [`&mut Map`](crate::maps::Map), and [`Map`](crate::maps::Map)
+//! types respectively. Those three types can be converted to *typed maps* using
+//! the [`TryFrom`](std::convert::TryFrom) or [`TryInto`](std::convert::TryInto)
+//! trait. For example:
 //!
 //! ```no_run
 //! # let mut bpf = aya::Bpf::load(&[])?;
 //! use aya::maps::SockMap;
 //! use aya::programs::SkMsg;
 //!
-//! let intercept_egress = SockMap::try_from(bpf.map_mut("INTERCEPT_EGRESS")?)?;
+//! let intercept_egress = SockMap::try_from(bpf.map_mut("INTERCEPT_EGRESS").unwrap())?;
+//! let map_fd = intercept_egress.fd()?;
 //! let prog: &mut SkMsg = bpf.program_mut("intercept_egress_packet").unwrap().try_into()?;
 //! prog.load()?;
-//! prog.attach(&intercept_egress)?;
+//! prog.attach(map_fd)?;
+//!
 //! # Ok::<(), aya::BpfError>(())
 //! ```
 //!
@@ -32,6 +37,7 @@
 //! versa. Because of that, all map values must be plain old data and therefore
 //! implement the [Pod] trait.
 use std::{
+    convert::{AsMut, AsRef},
     ffi::CString,
     fmt, io,
     marker::PhantomData,
@@ -58,8 +64,6 @@ use crate::{
     PinningType, Pod,
 };
 
-mod map_lock;
-
 pub mod array;
 pub mod bloom_filter;
 pub mod hash_map;
@@ -71,8 +75,12 @@ pub mod stack;
 pub mod stack_trace;
 
 pub use array::{Array, PerCpuArray, ProgramArray};
+pub use bloom_filter::BloomFilter;
 pub use hash_map::{HashMap, PerCpuHashMap};
-pub use map_lock::*;
+pub use lpm_trie::LpmTrie;
+#[cfg(feature = "async")]
+#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
+pub use perf::AsyncPerfEventArray;
 pub use perf::PerfEventArray;
 pub use queue::Queue;
 pub use sock::{SockHash, SockMap};
@@ -82,13 +90,6 @@ pub use stack_trace::StackTraceMap;
 #[derive(Error, Debug)]
 /// Errors occuring from working with Maps
 pub enum MapError {
-    /// Unable to find the map
-    #[error("map `{name}` not found ")]
-    MapNotFound {
-        /// Map name
-        name: String,
-    },
-
     /// Invalid map type encontered
     #[error("invalid map type {map_type}")]
     InvalidMapType {
@@ -174,20 +175,6 @@ pub enum MapError {
         io_error: io::Error,
     },
 
-    /// Map is borrowed mutably
-    #[error("map `{name}` is borrowed mutably")]
-    BorrowError {
-        /// Map name
-        name: String,
-    },
-
-    /// Map is already borrowed
-    #[error("map `{name}` is already borrowed")]
-    BorrowMutError {
-        /// Map name
-        name: String,
-    },
-
     /// Could not pin map by name
     #[error("map `{name:?}` requested pinning by name. pinning failed")]
     PinError {
@@ -243,11 +230,234 @@ fn maybe_warn_rlimit() {
     }
 }
 
+/// eBPF map types.
+#[derive(Debug)]
+pub enum Map {
+    /// A [`Array`] map
+    Array(MapData),
+    /// A [`PerCpuArray`] map
+    PerCpuArray(MapData),
+    /// A [`ProgramArray`] map
+    ProgramArray(MapData),
+    /// A [`HashMap`] map
+    HashMap(MapData),
+    /// A [`PerCpuHashMap`] map
+    PerCpuHashMap(MapData),
+    /// A [`PerfEventArray`] map
+    PerfEventArray(MapData),
+    /// A [`SockMap`] map
+    SockMap(MapData),
+    /// A [`SockHash`] map
+    SockHash(MapData),
+    /// A [`BloomFilter`] map
+    BloomFilter(MapData),
+    /// A [`LpmTrie`] map
+    LpmTrie(MapData),
+    /// A [`Stack`] map
+    Stack(MapData),
+    /// A [`StackTraceMap`] map
+    StackTraceMap(MapData),
+    /// A [`Queue`] map
+    Queue(MapData),
+}
+
+impl Map {
+    /// Returns the low level map type.
+    fn map_type(&self) -> u32 {
+        match self {
+            Map::Array(map) => map.obj.map_type(),
+            Map::PerCpuArray(map) => map.obj.map_type(),
+            Map::ProgramArray(map) => map.obj.map_type(),
+            Map::HashMap(map) => map.obj.map_type(),
+            Map::PerCpuHashMap(map) => map.obj.map_type(),
+            Map::PerfEventArray(map) => map.obj.map_type(),
+            Map::SockHash(map) => map.obj.map_type(),
+            Map::SockMap(map) => map.obj.map_type(),
+            Map::BloomFilter(map) => map.obj.map_type(),
+            Map::LpmTrie(map) => map.obj.map_type(),
+            Map::Stack(map) => map.obj.map_type(),
+            Map::StackTraceMap(map) => map.obj.map_type(),
+            Map::Queue(map) => map.obj.map_type(),
+        }
+    }
+}
+
+macro_rules! impl_try_from_map {
+    ($($tx:ident from Map::$ty:ident),+ $(,)?) => {
+        $(
+            impl<'a> TryFrom<&'a Map> for $tx<&'a MapData> {
+                type Error = MapError;
+
+                fn try_from(map: &'a Map) -> Result<$tx<&'a MapData>, MapError> {
+                    match map {
+                        Map::$ty(m) => {
+                            $tx::new(m)
+                        },
+                        _ => Err(MapError::InvalidMapType{ map_type: map.map_type()}),
+                    }
+                }
+            }
+
+            impl<'a,> TryFrom<&'a mut Map> for $tx<&'a mut MapData> {
+                type Error = MapError;
+
+                fn try_from(map: &'a mut Map) -> Result<$tx<&'a mut MapData>, MapError> {
+                    match map {
+                        Map::$ty(m) => {
+                            $tx::new(m)
+                        },
+                        _ => Err(MapError::InvalidMapType{ map_type: map.map_type()}),
+                    }
+                }
+            }
+
+            impl TryFrom<Map> for $tx<MapData> {
+                type Error = MapError;
+
+                fn try_from(map: Map) -> Result<$tx<MapData>, MapError> {
+                    match map {
+                        Map::$ty(m) => {
+                            $tx::new(m)
+                        },
+                        _ => Err(MapError::InvalidMapType{ map_type: map.map_type()}),
+                    }
+                }
+            }
+       )+
+   }
+}
+
+impl_try_from_map!(
+    ProgramArray from Map::ProgramArray,
+    SockMap from Map::SockMap,
+    PerfEventArray from Map::PerfEventArray,
+    StackTraceMap from Map::StackTraceMap,
+);
+
+#[cfg(feature = "async")]
+#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
+impl_try_from_map!(
+    AsyncPerfEventArray from Map::PerfEventArray,
+);
+
+macro_rules! impl_try_from_map_generic_key_or_value {
+    ($($ty:ident),+ $(,)?) => {
+        $(
+            impl<'a, V:Pod> TryFrom<&'a Map> for $ty<&'a MapData, V> {
+                type Error = MapError;
+
+                fn try_from(map: &'a Map) -> Result<$ty<&'a MapData , V>, MapError> {
+                    match map {
+                        Map::$ty(m) => {
+                            $ty::new(m)
+                        },
+                        _ => Err(MapError::InvalidMapType{ map_type: map.map_type()}),
+                    }
+                }
+            }
+
+            impl<'a,V: Pod> TryFrom<&'a mut Map> for $ty<&'a mut MapData, V> {
+                type Error = MapError;
+
+                fn try_from(map: &'a mut Map) -> Result<$ty<&'a mut MapData, V>, MapError> {
+                    match map {
+                        Map::$ty(m) => {
+                            $ty::new(m)
+                        },
+                        _ => Err(MapError::InvalidMapType{ map_type: map.map_type()}),
+                    }
+                }
+            }
+
+            impl<V: Pod> TryFrom<Map> for $ty<MapData, V> {
+                type Error = MapError;
+
+                fn try_from(map: Map) -> Result<$ty<MapData, V>, MapError> {
+                    match map {
+                        Map::$ty(m) => {
+                            $ty::new(m)
+                        },
+                        _ => Err(MapError::InvalidMapType{ map_type: map.map_type()}),
+                    }
+                }
+            }
+       )+
+   }
+}
+
+impl_try_from_map_generic_key_or_value!(Array, PerCpuArray, SockHash, BloomFilter, Queue, Stack,);
+
+macro_rules! impl_try_from_map_generic_key_and_value {
+    ($($ty:ident),+ $(,)?) => {
+        $(
+            impl<'a, V: Pod, K: Pod> TryFrom<&'a Map> for $ty<&'a MapData, V, K> {
+                type Error = MapError;
+
+                fn try_from(map: &'a Map) -> Result<$ty<&'a MapData,V,K>, MapError> {
+                    match map {
+                        Map::$ty(m) => {
+                            $ty::new(m)
+                        },
+                        _ => Err(MapError::InvalidMapType{ map_type: map.map_type()}),
+                    }
+                }
+            }
+
+            impl<'a,V: Pod,K: Pod> TryFrom<&'a mut Map> for $ty<&'a mut MapData, V, K> {
+                type Error = MapError;
+
+                fn try_from(map: &'a mut Map) -> Result<$ty<&'a mut MapData, V, K>, MapError> {
+                    match map {
+                        Map::$ty(m) => {
+                            $ty::new(m)
+                        },
+                        _ => Err(MapError::InvalidMapType{ map_type: map.map_type()}),
+                }
+            }
+            }
+       )+
+   }
+}
+
+impl_try_from_map_generic_key_and_value!(HashMap, PerCpuHashMap, LpmTrie);
+
+pub(crate) fn check_bounds(map: &MapData, index: u32) -> Result<(), MapError> {
+    let max_entries = map.obj.max_entries();
+    if index >= max_entries {
+        Err(MapError::OutOfBounds { index, max_entries })
+    } else {
+        Ok(())
+    }
+}
+
+pub(crate) fn check_kv_size<K, V>(map: &MapData) -> Result<(), MapError> {
+    let size = mem::size_of::<K>();
+    let expected = map.obj.key_size() as usize;
+    if size != expected {
+        return Err(MapError::InvalidKeySize { size, expected });
+    }
+    let size = mem::size_of::<V>();
+    let expected = map.obj.value_size() as usize;
+    if size != expected {
+        return Err(MapError::InvalidValueSize { size, expected });
+    };
+    Ok(())
+}
+
+pub(crate) fn check_v_size<V>(map: &MapData) -> Result<(), MapError> {
+    let size = mem::size_of::<V>();
+    let expected = map.obj.value_size() as usize;
+    if size != expected {
+        return Err(MapError::InvalidValueSize { size, expected });
+    };
+    Ok(())
+}
+
 /// A generic handle to a BPF map.
 ///
 /// You should never need to use this unless you're implementing a new map type.
 #[derive(Debug)]
-pub struct Map {
+pub struct MapData {
     pub(crate) obj: obj::Map,
     pub(crate) fd: Option<RawFd>,
     pub(crate) btf_fd: Option<RawFd>,
@@ -255,7 +465,19 @@ pub struct Map {
     pub pinned: bool,
 }
 
-impl Map {
+impl AsRef<MapData> for MapData {
+    fn as_ref(&self) -> &MapData {
+        self
+    }
+}
+
+impl AsMut<MapData> for MapData {
+    fn as_mut(&mut self) -> &mut MapData {
+        self
+    }
+}
+
+impl MapData {
     /// Creates a new map with the provided `name`
     pub fn create(&mut self, name: &str) -> Result<RawFd, MapError> {
         if self.fd.is_some() {
@@ -303,7 +525,7 @@ impl Map {
     }
 
     /// Loads a map from a pinned path in bpffs.
-    pub fn from_pin<P: AsRef<Path>>(path: P) -> Result<Map, MapError> {
+    pub fn from_pin<P: AsRef<Path>>(path: P) -> Result<MapData, MapError> {
         let path_string =
             CString::new(path.as_ref().to_string_lossy().into_owned()).map_err(|e| {
                 MapError::PinError {
@@ -324,7 +546,7 @@ impl Map {
             io_error,
         })?;
 
-        Ok(Map {
+        Ok(MapData {
             obj: parse_map_info(info, PinningType::ByName),
             fd: Some(fd),
             btf_fd: None,
@@ -334,16 +556,16 @@ impl Map {
 
     /// Loads a map from a [`RawFd`].
     ///
-    /// If loading from a BPF Filesystem (bpffs) you should use [`Map::from_pin`].
+    /// If loading from a BPF Filesystem (bpffs) you should use [`Map::from_pin`](crate::maps::MapData::from_pin).
     /// This API is intended for cases where you have received a valid BPF FD from some other means.
     /// For example, you received an FD over Unix Domain Socket.
-    pub fn from_fd(fd: RawFd) -> Result<Map, MapError> {
+    pub fn from_fd(fd: RawFd) -> Result<MapData, MapError> {
         let info = bpf_map_get_info_by_fd(fd).map_err(|io_error| MapError::SyscallError {
             call: "BPF_OBJ_GET".to_owned(),
             io_error,
         })?;
 
-        Ok(Map {
+        Ok(MapData {
             obj: parse_map_info(info, PinningType::None),
             fd: Some(fd),
             btf_fd: None,
@@ -351,11 +573,6 @@ impl Map {
         })
     }
 
-    /// Returns the [`bpf_map_type`] of this map
-    pub fn map_type(&self) -> Result<bpf_map_type, MapError> {
-        bpf_map_type::try_from(self.obj.map_type())
-    }
-
     pub(crate) fn fd_or_err(&self) -> Result<RawFd, MapError> {
         self.fd.ok_or(MapError::NotCreated)
     }
@@ -389,7 +606,7 @@ impl Map {
     }
 }
 
-impl Drop for Map {
+impl Drop for MapData {
     fn drop(&mut self) {
         // TODO: Replace this with an OwnedFd once that is stabilized.
         if let Some(fd) = self.fd.take() {
@@ -398,10 +615,26 @@ impl Drop for Map {
     }
 }
 
+impl Clone for MapData {
+    fn clone(&self) -> MapData {
+        MapData {
+            obj: self.obj.clone(),
+            fd: {
+                if let Some(fd) = self.fd {
+                    unsafe { Some(libc::dup(fd)) };
+                }
+                None
+            },
+            btf_fd: self.btf_fd,
+            pinned: self.pinned,
+        }
+    }
+}
+
 /// An iterable map
 pub trait IterableMap<K: Pod, V> {
     /// Get a generic map handle
-    fn map(&self) -> &Map;
+    fn map(&self) -> &MapData;
 
     /// Get the value for the provided `key`
     fn get(&self, key: &K) -> Result<V, MapError>;
@@ -409,13 +642,13 @@ pub trait IterableMap<K: Pod, V> {
 
 /// Iterator returned by `map.keys()`.
 pub struct MapKeys<'coll, K: Pod> {
-    map: &'coll Map,
+    map: &'coll MapData,
     err: bool,
     key: Option<K>,
 }
 
 impl<'coll, K: Pod> MapKeys<'coll, K> {
-    fn new(map: &'coll Map) -> MapKeys<'coll, K> {
+    fn new(map: &'coll MapData) -> MapKeys<'coll, K> {
         MapKeys {
             map,
             err: false,
@@ -538,6 +771,7 @@ impl TryFrom<u32> for bpf_map_type {
         })
     }
 }
+
 pub(crate) struct PerCpuKernelMem {
     bytes: Vec<u8>,
 }
@@ -643,6 +877,7 @@ mod tests {
     use crate::{
         bpf_map_def,
         generated::{bpf_cmd, bpf_map_type::BPF_MAP_TYPE_HASH},
+        maps::MapData,
         obj::MapKind,
         sys::{override_syscall, Syscall},
     };
@@ -665,8 +900,8 @@ mod tests {
         })
     }
 
-    fn new_map() -> Map {
-        Map {
+    fn new_map() -> MapData {
+        MapData {
             obj: new_obj_map(),
             fd: None,
             pinned: false,
diff --git a/aya/src/maps/perf/async_perf_event_array.rs b/aya/src/maps/perf/async_perf_event_array.rs
index 1c14fc6a2..39ffcfa18 100644
--- a/aya/src/maps/perf/async_perf_event_array.rs
+++ b/aya/src/maps/perf/async_perf_event_array.rs
@@ -1,6 +1,6 @@
 use bytes::BytesMut;
 use std::{
-    ops::DerefMut,
+    convert::AsMut,
     os::unix::prelude::{AsRawFd, RawFd},
 };
 
@@ -12,7 +12,7 @@ use tokio::io::unix::AsyncFd;
 
 use crate::maps::{
     perf::{Events, PerfBufferError, PerfEventArray, PerfEventArrayBuffer},
-    Map, MapError, MapRefMut,
+    MapData, MapError,
 };
 
 /// A `Future` based map that can be used to receive events from eBPF programs using the linux
@@ -45,7 +45,7 @@ use crate::maps::{
 /// # }
 /// # #[cfg(feature = "async_tokio")]
 /// # async fn try_main() -> Result<(), Error> {
-/// # let bpf = aya::Bpf::load(&[])?;
+/// # let mut bpf = aya::Bpf::load(&[])?;
 /// use aya::maps::perf::{AsyncPerfEventArray, PerfBufferError};
 /// use aya::util::online_cpus;
 /// use futures::future;
@@ -53,7 +53,7 @@ use crate::maps::{
 /// use tokio::task; // or async_std::task
 ///
 /// // try to convert the PERF_ARRAY map to an AsyncPerfEventArray
-/// let mut perf_array = AsyncPerfEventArray::try_from(bpf.map_mut("PERF_ARRAY")?)?;
+/// let mut perf_array = AsyncPerfEventArray::try_from(bpf.take_map("PERF_ARRAY").unwrap())?;
 ///
 /// for cpu_id in online_cpus()? {
 ///     // open a separate perf buffer for each cpu
@@ -85,11 +85,11 @@ use crate::maps::{
 /// # }
 /// ```
 #[doc(alias = "BPF_MAP_TYPE_PERF_EVENT_ARRAY")]
-pub struct AsyncPerfEventArray<T: DerefMut<Target = Map>> {
+pub struct AsyncPerfEventArray<T> {
     perf_map: PerfEventArray<T>,
 }
 
-impl<T: DerefMut<Target = Map>> AsyncPerfEventArray<T> {
+impl<T: AsMut<MapData> + AsRef<MapData>> AsyncPerfEventArray<T> {
     /// Opens the perf buffer at the given index.
     ///
     /// The returned buffer will receive all the events eBPF programs send at the given index.
@@ -112,8 +112,8 @@ impl<T: DerefMut<Target = Map>> AsyncPerfEventArray<T> {
     }
 }
 
-impl<T: DerefMut<Target = Map>> AsyncPerfEventArray<T> {
-    fn new(map: T) -> Result<AsyncPerfEventArray<T>, MapError> {
+impl<T: AsRef<MapData>> AsyncPerfEventArray<T> {
+    pub(crate) fn new(map: T) -> Result<AsyncPerfEventArray<T>, MapError> {
         Ok(AsyncPerfEventArray {
             perf_map: PerfEventArray::new(map)?,
         })
@@ -127,7 +127,7 @@ impl<T: DerefMut<Target = Map>> AsyncPerfEventArray<T> {
 ///
 /// See the [`AsyncPerfEventArray` documentation](AsyncPerfEventArray) for an overview of how to
 /// use perf buffers.
-pub struct AsyncPerfEventArrayBuffer<T: DerefMut<Target = Map>> {
+pub struct AsyncPerfEventArrayBuffer<T> {
     buf: PerfEventArrayBuffer<T>,
 
     #[cfg(feature = "async_tokio")]
@@ -138,7 +138,7 @@ pub struct AsyncPerfEventArrayBuffer<T: DerefMut<Target = Map>> {
 }
 
 #[cfg(any(feature = "async_tokio"))]
-impl<T: DerefMut<Target = Map>> AsyncPerfEventArrayBuffer<T> {
+impl<T: AsMut<MapData> + AsRef<MapData>> AsyncPerfEventArrayBuffer<T> {
     /// Reads events from the buffer.
     ///
     /// This method reads events into the provided slice of buffers, filling
@@ -168,7 +168,7 @@ impl<T: DerefMut<Target = Map>> AsyncPerfEventArrayBuffer<T> {
 }
 
 #[cfg(all(not(feature = "async_tokio"), feature = "async_std"))]
-impl<T: DerefMut<Target = Map>> AsyncPerfEventArrayBuffer<T> {
+impl<T: AsMut<MapData> + AsRef<MapData>> AsyncPerfEventArrayBuffer<T> {
     /// Reads events from the buffer.
     ///
     /// This method reads events into the provided slice of buffers, filling
@@ -195,11 +195,3 @@ impl<T: DerefMut<Target = Map>> AsyncPerfEventArrayBuffer<T> {
         }
     }
 }
-
-impl TryFrom<MapRefMut> for AsyncPerfEventArray<MapRefMut> {
-    type Error = MapError;
-
-    fn try_from(a: MapRefMut) -> Result<AsyncPerfEventArray<MapRefMut>, MapError> {
-        AsyncPerfEventArray::new(a)
-    }
-}
diff --git a/aya/src/maps/perf/mod.rs b/aya/src/maps/perf/mod.rs
index 1f9bae7d2..db3c30058 100644
--- a/aya/src/maps/perf/mod.rs
+++ b/aya/src/maps/perf/mod.rs
@@ -1,6 +1,6 @@
 //! Ring buffer types used to receive events from eBPF programs using the linux `perf` API.
 //!
-//! See the [`PerfEventArray`] and [`AsyncPerfEventArray`].
+//! See the [`PerfEventArray`](crate::maps::PerfEventArray) and [`AsyncPerfEventArray`](crate::maps::perf::AsyncPerfEventArray).
 #[cfg(any(feature = "async"))]
 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
 mod async_perf_event_array;
diff --git a/aya/src/maps/perf/perf_event_array.rs b/aya/src/maps/perf/perf_event_array.rs
index 2a74a4372..8dafd7f1e 100644
--- a/aya/src/maps/perf/perf_event_array.rs
+++ b/aya/src/maps/perf/perf_event_array.rs
@@ -2,7 +2,8 @@
 //!
 //! [`perf`]: https://perf.wiki.kernel.org/index.php/Main_Page.
 use std::{
-    ops::DerefMut,
+    convert::AsMut,
+    ops::Deref,
     os::unix::io::{AsRawFd, RawFd},
     sync::Arc,
 };
@@ -10,10 +11,9 @@ use std::{
 use bytes::BytesMut;
 
 use crate::{
-    generated::bpf_map_type::BPF_MAP_TYPE_PERF_EVENT_ARRAY,
     maps::{
         perf::{Events, PerfBuffer, PerfBufferError},
-        Map, MapError, MapRefMut,
+        MapData, MapError,
     },
     sys::bpf_map_update_elem,
     util::page_size,
@@ -26,12 +26,12 @@ use crate::{
 ///
 /// See the [`PerfEventArray` documentation](PerfEventArray) for an overview of how to use
 /// perf buffers.
-pub struct PerfEventArrayBuffer<T: DerefMut<Target = Map>> {
+pub struct PerfEventArrayBuffer<T> {
     _map: Arc<T>,
     buf: PerfBuffer,
 }
 
-impl<T: DerefMut<Target = Map>> PerfEventArrayBuffer<T> {
+impl<T: AsMut<MapData> + AsRef<MapData>> PerfEventArrayBuffer<T> {
     /// Returns true if the buffer contains events that haven't been read.
     pub fn readable(&self) -> bool {
         self.buf.readable()
@@ -55,7 +55,7 @@ impl<T: DerefMut<Target = Map>> PerfEventArrayBuffer<T> {
     }
 }
 
-impl<T: DerefMut<Target = Map>> AsRawFd for PerfEventArrayBuffer<T> {
+impl<T: AsMut<MapData> + AsRef<MapData>> AsRawFd for PerfEventArrayBuffer<T> {
     fn as_raw_fd(&self) -> RawFd {
         self.buf.as_raw_fd()
     }
@@ -83,15 +83,15 @@ impl<T: DerefMut<Target = Map>> AsRawFd for PerfEventArrayBuffer<T> {
 ///
 /// ```no_run
 /// # use aya::maps::perf::PerfEventArrayBuffer;
-/// # use aya::maps::Map;
-/// # use std::ops::DerefMut;
+/// # use aya::maps::MapData;
+/// # use std::convert::AsMut;
 /// # struct Poll<T> { _t: std::marker::PhantomData<T> };
-/// # impl<T: DerefMut<Target=Map>> Poll<T> {
+/// # impl<T: AsMut<MapData>> Poll<T> {
 /// #    fn poll_readable(&self) -> &mut [PerfEventArrayBuffer<T>] {
 /// #        &mut []
 /// #    }
 /// # }
-/// # fn poll_buffers<T: DerefMut<Target=Map>>(bufs: Vec<PerfEventArrayBuffer<T>>) -> Poll<T> {
+/// # fn poll_buffers<T: AsMut<MapData>>(bufs: Vec<PerfEventArrayBuffer<T>>) -> Poll<T> {
 /// #    Poll { _t: std::marker::PhantomData }
 /// # }
 /// # #[derive(thiserror::Error, Debug)]
@@ -105,12 +105,12 @@ impl<T: DerefMut<Target = Map>> AsRawFd for PerfEventArrayBuffer<T> {
 /// #    #[error(transparent)]
 /// #    PerfBuf(#[from] aya::maps::perf::PerfBufferError),
 /// # }
-/// # let bpf = aya::Bpf::load(&[])?;
+/// # let mut bpf = aya::Bpf::load(&[])?;
 /// use aya::maps::PerfEventArray;
 /// use aya::util::online_cpus;
 /// use bytes::BytesMut;
 ///
-/// let mut perf_array = PerfEventArray::try_from(bpf.map_mut("EVENTS")?)?;
+/// let mut perf_array = PerfEventArray::try_from(bpf.map_mut("EVENTS").unwrap())?;
 ///
 /// // eBPF programs are going to write to the EVENTS perf array, using the id of the CPU they're
 /// // running on as the array index.
@@ -155,25 +155,23 @@ impl<T: DerefMut<Target = Map>> AsRawFd for PerfEventArrayBuffer<T> {
 /// [tokio]: https://docs.rs/tokio
 /// [async-std]: https://docs.rs/async-std
 #[doc(alias = "BPF_MAP_TYPE_PERF_EVENT_ARRAY")]
-pub struct PerfEventArray<T: DerefMut<Target = Map>> {
+pub struct PerfEventArray<T> {
     map: Arc<T>,
     page_size: usize,
 }
 
-impl<T: DerefMut<Target = Map>> PerfEventArray<T> {
+impl<T: AsRef<MapData>> PerfEventArray<T> {
     pub(crate) fn new(map: T) -> Result<PerfEventArray<T>, MapError> {
-        let map_type = map.obj.map_type();
-        if map_type != BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32 {
-            return Err(MapError::InvalidMapType { map_type });
-        }
-        let _fd = map.fd_or_err()?;
+        let _fd = map.as_ref().fd_or_err()?;
 
         Ok(PerfEventArray {
             map: Arc::new(map),
             page_size: page_size(),
         })
     }
+}
 
+impl<T: AsMut<MapData> + AsRef<MapData>> PerfEventArray<T> {
     /// Opens the perf buffer at the given index.
     ///
     /// The returned buffer will receive all the events eBPF programs send at the given index.
@@ -185,7 +183,8 @@ impl<T: DerefMut<Target = Map>> PerfEventArray<T> {
         // FIXME: keep track of open buffers
 
         // this cannot fail as new() checks that the fd is open
-        let map_fd = self.map.fd_or_err().unwrap();
+        let map_data: &MapData = self.map.deref().as_ref();
+        let map_fd = map_data.fd_or_err().unwrap();
         let buf = PerfBuffer::open(index, self.page_size, page_count.unwrap_or(2))?;
         bpf_map_update_elem(map_fd, Some(&index), &buf.as_raw_fd(), 0)
             .map_err(|(_, io_error)| io_error)?;
@@ -196,11 +195,3 @@ impl<T: DerefMut<Target = Map>> PerfEventArray<T> {
         })
     }
 }
-
-impl TryFrom<MapRefMut> for PerfEventArray<MapRefMut> {
-    type Error = MapError;
-
-    fn try_from(a: MapRefMut) -> Result<PerfEventArray<MapRefMut>, MapError> {
-        PerfEventArray::new(a)
-    }
-}
diff --git a/aya/src/maps/queue.rs b/aya/src/maps/queue.rs
index f8fb56b03..e72337731 100644
--- a/aya/src/maps/queue.rs
+++ b/aya/src/maps/queue.rs
@@ -1,13 +1,11 @@
 //! A FIFO queue.
 use std::{
+    convert::{AsMut, AsRef},
     marker::PhantomData,
-    mem,
-    ops::{Deref, DerefMut},
 };
 
 use crate::{
-    generated::bpf_map_type::BPF_MAP_TYPE_QUEUE,
-    maps::{Map, MapError, MapRef, MapRefMut},
+    maps::{check_kv_size, MapData, MapError},
     sys::{bpf_map_lookup_and_delete_elem, bpf_map_push_elem},
     Pod,
 };
@@ -20,39 +18,27 @@ use crate::{
 ///
 /// # Examples
 /// ```no_run
-/// # let bpf = aya::Bpf::load(&[])?;
+/// # let mut bpf = aya::Bpf::load(&[])?;
 /// use aya::maps::Queue;
 ///
-/// let mut queue = Queue::try_from(bpf.map_mut("ARRAY")?)?;
+/// let mut queue = Queue::try_from(bpf.map_mut("ARRAY").unwrap())?;
 /// queue.push(42, 0)?;
 /// queue.push(43, 0)?;
 /// assert_eq!(queue.pop(0)?, 42);
 /// # Ok::<(), aya::BpfError>(())
 /// ```
 #[doc(alias = "BPF_MAP_TYPE_QUEUE")]
-pub struct Queue<T: Deref<Target = Map>, V: Pod> {
+pub struct Queue<T, V: Pod> {
     inner: T,
     _v: PhantomData<V>,
 }
 
-impl<T: Deref<Target = Map>, V: Pod> Queue<T, V> {
-    fn new(map: T) -> Result<Queue<T, V>, MapError> {
-        let map_type = map.obj.map_type();
-        if map_type != BPF_MAP_TYPE_QUEUE as u32 {
-            return Err(MapError::InvalidMapType { map_type });
-        }
-        let expected = 0;
-        let size = map.obj.key_size() as usize;
-        if size != expected {
-            return Err(MapError::InvalidKeySize { size, expected });
-        }
+impl<T: AsRef<MapData>, V: Pod> Queue<T, V> {
+    pub(crate) fn new(map: T) -> Result<Queue<T, V>, MapError> {
+        let data = map.as_ref();
+        check_kv_size::<(), V>(data)?;
 
-        let expected = mem::size_of::<V>();
-        let size = map.obj.value_size() as usize;
-        if size != expected {
-            return Err(MapError::InvalidValueSize { size, expected });
-        }
-        let _fd = map.fd_or_err()?;
+        let _fd = data.fd_or_err()?;
 
         Ok(Queue {
             inner: map,
@@ -64,11 +50,11 @@ impl<T: Deref<Target = Map>, V: Pod> Queue<T, V> {
     ///
     /// This corresponds to the value of `bpf_map_def::max_entries` on the eBPF side.
     pub fn capacity(&self) -> u32 {
-        self.inner.obj.max_entries()
+        self.inner.as_ref().obj.max_entries()
     }
 }
 
-impl<T: Deref<Target = Map> + DerefMut<Target = Map>, V: Pod> Queue<T, V> {
+impl<T: AsMut<MapData>, V: Pod> Queue<T, V> {
     /// Removes the first element and returns it.
     ///
     /// # Errors
@@ -76,7 +62,7 @@ impl<T: Deref<Target = Map> + DerefMut<Target = Map>, V: Pod> Queue<T, V> {
     /// Returns [`MapError::ElementNotFound`] if the queue is empty, [`MapError::SyscallError`]
     /// if `bpf_map_lookup_and_delete_elem` fails.
     pub fn pop(&mut self, flags: u64) -> Result<V, MapError> {
-        let fd = self.inner.fd_or_err()?;
+        let fd = self.inner.as_mut().fd_or_err()?;
 
         let value = bpf_map_lookup_and_delete_elem::<u32, _>(fd, None, flags).map_err(
             |(_, io_error)| MapError::SyscallError {
@@ -93,7 +79,7 @@ impl<T: Deref<Target = Map> + DerefMut<Target = Map>, V: Pod> Queue<T, V> {
     ///
     /// [`MapError::SyscallError`] if `bpf_map_update_elem` fails.
     pub fn push(&mut self, value: V, flags: u64) -> Result<(), MapError> {
-        let fd = self.inner.fd_or_err()?;
+        let fd = self.inner.as_mut().fd_or_err()?;
         bpf_map_push_elem(fd, &value, flags).map_err(|(_, io_error)| MapError::SyscallError {
             call: "bpf_map_push_elem".to_owned(),
             io_error,
@@ -101,19 +87,3 @@ impl<T: Deref<Target = Map> + DerefMut<Target = Map>, V: Pod> Queue<T, V> {
         Ok(())
     }
 }
-
-impl<V: Pod> TryFrom<MapRef> for Queue<MapRef, V> {
-    type Error = MapError;
-
-    fn try_from(a: MapRef) -> Result<Queue<MapRef, V>, MapError> {
-        Queue::new(a)
-    }
-}
-
-impl<V: Pod> TryFrom<MapRefMut> for Queue<MapRefMut, V> {
-    type Error = MapError;
-
-    fn try_from(a: MapRefMut) -> Result<Queue<MapRefMut, V>, MapError> {
-        Queue::new(a)
-    }
-}
diff --git a/aya/src/maps/sock/mod.rs b/aya/src/maps/sock/mod.rs
index ebc37b256..ffbf7aa31 100644
--- a/aya/src/maps/sock/mod.rs
+++ b/aya/src/maps/sock/mod.rs
@@ -2,15 +2,17 @@
 mod sock_hash;
 mod sock_map;
 
-use std::os::unix::io::RawFd;
-
-use crate::maps::MapError;
-
 pub use sock_hash::SockHash;
 pub use sock_map::SockMap;
 
-/// Shared behaviour between [`SockHash`] and [`SockMap`]
-pub trait SocketMap {
-    /// Returns a [`Result`] containg the map fd or an error if there is none
-    fn fd_or_err(&self) -> Result<RawFd, MapError>;
+use std::os::unix::io::{AsRawFd, RawFd};
+
+/// A socket map file descriptor.
+#[derive(Copy, Clone)]
+pub struct SockMapFd(RawFd);
+
+impl AsRawFd for SockMapFd {
+    fn as_raw_fd(&self) -> RawFd {
+        self.0
+    }
 }
diff --git a/aya/src/maps/sock/sock_hash.rs b/aya/src/maps/sock/sock_hash.rs
index e6350f78d..4271f5e64 100644
--- a/aya/src/maps/sock/sock_hash.rs
+++ b/aya/src/maps/sock/sock_hash.rs
@@ -1,13 +1,12 @@
 use std::{
+    convert::{AsMut, AsRef},
     marker::PhantomData,
-    ops::{Deref, DerefMut},
     os::unix::io::{AsRawFd, RawFd},
 };
 
 use crate::{
-    generated::bpf_map_type::BPF_MAP_TYPE_SOCKHASH,
     maps::{
-        hash_map, sock::SocketMap, IterableMap, Map, MapError, MapIter, MapKeys, MapRef, MapRefMut,
+        check_kv_size, hash_map, sock::SockMapFd, IterableMap, MapData, MapError, MapIter, MapKeys,
     },
     sys::bpf_map_lookup_elem,
     Pod,
@@ -47,12 +46,16 @@ use crate::{
 /// use aya::maps::SockHash;
 /// use aya::programs::SkMsg;
 ///
-/// let mut intercept_egress = SockHash::try_from(bpf.map_mut("INTERCEPT_EGRESS")?)?;
+/// let mut intercept_egress = SockHash::<_, u32>::try_from(bpf.map("INTERCEPT_EGRESS").unwrap())?;
+/// let map_fd = intercept_egress.fd()?;
+///
 /// let prog: &mut SkMsg = bpf.program_mut("intercept_egress_packet").unwrap().try_into()?;
 /// prog.load()?;
-/// prog.attach(&intercept_egress)?;
+/// prog.attach(map_fd)?;
 ///
 /// let mut client = TcpStream::connect("127.0.0.1:1234")?;
+/// let mut intercept_egress = SockHash::try_from(bpf.map_mut("INTERCEPT_EGRESS").unwrap())?;
+///
 /// intercept_egress.insert(1234, client.as_raw_fd(), 0)?;
 ///
 /// // the write will be intercepted
@@ -60,21 +63,16 @@ use crate::{
 /// # Ok::<(), Error>(())
 /// ```
 #[doc(alias = "BPF_MAP_TYPE_SOCKHASH")]
-pub struct SockHash<T: Deref<Target = Map>, K> {
+pub struct SockHash<T, K> {
     inner: T,
     _k: PhantomData<K>,
 }
 
-impl<T: Deref<Target = Map>, K: Pod> SockHash<T, K> {
+impl<T: AsRef<MapData>, K: Pod> SockHash<T, K> {
     pub(crate) fn new(map: T) -> Result<SockHash<T, K>, MapError> {
-        let map_type = map.obj.map_type();
-
-        // validate the map definition
-        if map_type != BPF_MAP_TYPE_SOCKHASH as u32 {
-            return Err(MapError::InvalidMapType { map_type });
-        }
-        hash_map::check_kv_size::<K, u32>(&map)?;
-        let _ = map.fd_or_err()?;
+        let data = map.as_ref();
+        check_kv_size::<K, u32>(data)?;
+        let _ = data.fd_or_err()?;
 
         Ok(SockHash {
             inner: map,
@@ -84,7 +82,7 @@ impl<T: Deref<Target = Map>, K: Pod> SockHash<T, K> {
 
     /// Returns the fd of the socket stored at the given key.
     pub fn get(&self, key: &K, flags: u64) -> Result<RawFd, MapError> {
-        let fd = self.inner.deref().fd_or_err()?;
+        let fd = self.inner.as_ref().fd_or_err()?;
         let value = bpf_map_lookup_elem(fd, key, flags).map_err(|(_, io_error)| {
             MapError::SyscallError {
                 call: "bpf_map_lookup_elem".to_owned(),
@@ -103,50 +101,36 @@ impl<T: Deref<Target = Map>, K: Pod> SockHash<T, K> {
     /// An iterator visiting all keys in arbitrary order. The iterator element
     /// type is `Result<K, MapError>`.
     pub fn keys(&self) -> MapKeys<'_, K> {
-        MapKeys::new(&self.inner)
+        MapKeys::new(self.inner.as_ref())
+    }
+
+    /// Returns the map's file descriptor.
+    ///
+    /// The returned file descriptor can be used to attach programs that work with
+    /// socket maps, like [`SkMsg`](crate::programs::SkMsg) and [`SkSkb`](crate::programs::SkSkb).
+    pub fn fd(&self) -> Result<SockMapFd, MapError> {
+        Ok(SockMapFd(self.inner.as_ref().fd_or_err()?))
     }
 }
 
-impl<T: DerefMut<Target = Map>, K: Pod> SockHash<T, K> {
+impl<T: AsMut<MapData>, K: Pod> SockHash<T, K> {
     /// Inserts a socket under the given key.
     pub fn insert<I: AsRawFd>(&mut self, key: K, value: I, flags: u64) -> Result<(), MapError> {
-        hash_map::insert(&mut self.inner, key, value.as_raw_fd(), flags)
+        hash_map::insert(self.inner.as_mut(), key, value.as_raw_fd(), flags)
     }
 
     /// Removes a socket from the map.
     pub fn remove(&mut self, key: &K) -> Result<(), MapError> {
-        hash_map::remove(&mut self.inner, key)
+        hash_map::remove(self.inner.as_mut(), key)
     }
 }
 
-impl<T: Deref<Target = Map>, K: Pod> IterableMap<K, RawFd> for SockHash<T, K> {
-    fn map(&self) -> &Map {
-        &self.inner
+impl<T: AsRef<MapData>, K: Pod> IterableMap<K, RawFd> for SockHash<T, K> {
+    fn map(&self) -> &MapData {
+        self.inner.as_ref()
     }
 
     fn get(&self, key: &K) -> Result<RawFd, MapError> {
         SockHash::get(self, key, 0)
     }
 }
-
-impl<T: DerefMut<Target = Map>, K: Pod> SocketMap for SockHash<T, K> {
-    fn fd_or_err(&self) -> Result<RawFd, MapError> {
-        self.inner.fd_or_err()
-    }
-}
-
-impl<K: Pod> TryFrom<MapRef> for SockHash<MapRef, K> {
-    type Error = MapError;
-
-    fn try_from(a: MapRef) -> Result<SockHash<MapRef, K>, MapError> {
-        SockHash::new(a)
-    }
-}
-
-impl<K: Pod> TryFrom<MapRefMut> for SockHash<MapRefMut, K> {
-    type Error = MapError;
-
-    fn try_from(a: MapRefMut) -> Result<SockHash<MapRefMut, K>, MapError> {
-        SockHash::new(a)
-    }
-}
diff --git a/aya/src/maps/sock/sock_map.rs b/aya/src/maps/sock/sock_map.rs
index 542f6102b..28c5b4048 100644
--- a/aya/src/maps/sock/sock_map.rs
+++ b/aya/src/maps/sock/sock_map.rs
@@ -1,14 +1,12 @@
 //! An array of eBPF program file descriptors used as a jump table.
 
 use std::{
-    mem,
-    ops::{Deref, DerefMut},
+    convert::{AsMut, AsRef},
     os::unix::{io::AsRawFd, prelude::RawFd},
 };
 
 use crate::{
-    generated::bpf_map_type::BPF_MAP_TYPE_SOCKMAP,
-    maps::{sock::SocketMap, Map, MapError, MapKeys, MapRef, MapRefMut},
+    maps::{check_bounds, check_kv_size, sock::SockMapFd, MapData, MapError, MapKeys},
     sys::{bpf_map_delete_elem, bpf_map_update_elem},
 };
 
@@ -32,35 +30,26 @@ use crate::{
 /// use aya::maps::SockMap;
 /// use aya::programs::SkSkb;
 ///
-/// let intercept_ingress = SockMap::try_from(bpf.map_mut("INTERCEPT_INGRESS")?)?;
+/// let intercept_ingress = SockMap::try_from(bpf.map("INTERCEPT_INGRESS").unwrap())?;
+/// let map_fd = intercept_ingress.fd()?;
+///
 /// let prog: &mut SkSkb = bpf.program_mut("intercept_ingress_packet").unwrap().try_into()?;
 /// prog.load()?;
-/// prog.attach(&intercept_ingress)?;
+/// prog.attach(map_fd)?;
+///
 /// # Ok::<(), aya::BpfError>(())
 /// ```
 #[doc(alias = "BPF_MAP_TYPE_SOCKMAP")]
-pub struct SockMap<T: Deref<Target = Map>> {
+pub struct SockMap<T> {
     pub(crate) inner: T,
 }
 
-impl<T: Deref<Target = Map>> SockMap<T> {
-    fn new(map: T) -> Result<SockMap<T>, MapError> {
-        let map_type = map.obj.map_type();
-        if map_type != BPF_MAP_TYPE_SOCKMAP as u32 {
-            return Err(MapError::InvalidMapType { map_type });
-        }
-        let expected = mem::size_of::<u32>();
-        let size = map.obj.key_size() as usize;
-        if size != expected {
-            return Err(MapError::InvalidKeySize { size, expected });
-        }
+impl<T: AsRef<MapData>> SockMap<T> {
+    pub(crate) fn new(map: T) -> Result<SockMap<T>, MapError> {
+        let data = map.as_ref();
+        check_kv_size::<u32, RawFd>(data)?;
 
-        let expected = mem::size_of::<RawFd>();
-        let size = map.obj.value_size() as usize;
-        if size != expected {
-            return Err(MapError::InvalidValueSize { size, expected });
-        }
-        let _fd = map.fd_or_err()?;
+        let _fd = data.fd_or_err()?;
 
         Ok(SockMap { inner: map })
     }
@@ -68,24 +57,24 @@ impl<T: Deref<Target = Map>> SockMap<T> {
     /// An iterator over the indices of the array that point to a program. The iterator item type
     /// is `Result<u32, MapError>`.
     pub fn indices(&self) -> MapKeys<'_, u32> {
-        MapKeys::new(&self.inner)
+        MapKeys::new(self.inner.as_ref())
     }
 
-    fn check_bounds(&self, index: u32) -> Result<(), MapError> {
-        let max_entries = self.inner.obj.max_entries();
-        if index >= self.inner.obj.max_entries() {
-            Err(MapError::OutOfBounds { index, max_entries })
-        } else {
-            Ok(())
-        }
+    /// Returns the map's file descriptor.
+    ///
+    /// The returned file descriptor can be used to attach programs that work with
+    /// socket maps, like [`SkMsg`](crate::programs::SkMsg) and [`SkSkb`](crate::programs::SkSkb).
+    pub fn fd(&self) -> Result<SockMapFd, MapError> {
+        Ok(SockMapFd(self.inner.as_ref().fd_or_err()?))
     }
 }
 
-impl<T: Deref<Target = Map> + DerefMut<Target = Map>> SockMap<T> {
+impl<T: AsMut<MapData>> SockMap<T> {
     /// Stores a socket into the map.
     pub fn set<I: AsRawFd>(&mut self, index: u32, socket: &I, flags: u64) -> Result<(), MapError> {
-        let fd = self.inner.fd_or_err()?;
-        self.check_bounds(index)?;
+        let data = self.inner.as_mut();
+        let fd = data.fd_or_err()?;
+        check_bounds(data, index)?;
         bpf_map_update_elem(fd, Some(&index), &socket.as_raw_fd(), flags).map_err(
             |(_, io_error)| MapError::SyscallError {
                 call: "bpf_map_update_elem".to_owned(),
@@ -97,8 +86,9 @@ impl<T: Deref<Target = Map> + DerefMut<Target = Map>> SockMap<T> {
 
     /// Removes the socket stored at `index` from the map.
     pub fn clear_index(&mut self, index: &u32) -> Result<(), MapError> {
-        let fd = self.inner.fd_or_err()?;
-        self.check_bounds(*index)?;
+        let data = self.inner.as_mut();
+        let fd = data.fd_or_err()?;
+        check_bounds(data, *index)?;
         bpf_map_delete_elem(fd, index)
             .map(|_| ())
             .map_err(|(_, io_error)| MapError::SyscallError {
@@ -107,25 +97,3 @@ impl<T: Deref<Target = Map> + DerefMut<Target = Map>> SockMap<T> {
             })
     }
 }
-
-impl<T: Deref<Target = Map> + DerefMut<Target = Map>> SocketMap for SockMap<T> {
-    fn fd_or_err(&self) -> Result<RawFd, MapError> {
-        self.inner.fd_or_err()
-    }
-}
-
-impl TryFrom<MapRef> for SockMap<MapRef> {
-    type Error = MapError;
-
-    fn try_from(a: MapRef) -> Result<SockMap<MapRef>, MapError> {
-        SockMap::new(a)
-    }
-}
-
-impl TryFrom<MapRefMut> for SockMap<MapRefMut> {
-    type Error = MapError;
-
-    fn try_from(a: MapRefMut) -> Result<SockMap<MapRefMut>, MapError> {
-        SockMap::new(a)
-    }
-}
diff --git a/aya/src/maps/stack.rs b/aya/src/maps/stack.rs
index 601c11946..1350ef30b 100644
--- a/aya/src/maps/stack.rs
+++ b/aya/src/maps/stack.rs
@@ -1,13 +1,11 @@
 //! A LIFO stack.
 use std::{
+    convert::{AsMut, AsRef},
     marker::PhantomData,
-    mem,
-    ops::{Deref, DerefMut},
 };
 
 use crate::{
-    generated::bpf_map_type::BPF_MAP_TYPE_STACK,
-    maps::{Map, MapError, MapRef, MapRefMut},
+    maps::{check_kv_size, MapData, MapError},
     sys::{bpf_map_lookup_and_delete_elem, bpf_map_update_elem},
     Pod,
 };
@@ -20,39 +18,27 @@ use crate::{
 ///
 /// # Examples
 /// ```no_run
-/// # let bpf = aya::Bpf::load(&[])?;
+/// # let mut bpf = aya::Bpf::load(&[])?;
 /// use aya::maps::Stack;
 ///
-/// let mut stack = Stack::try_from(bpf.map_mut("STACK")?)?;
+/// let mut stack = Stack::try_from(bpf.map_mut("STACK").unwrap())?;
 /// stack.push(42, 0)?;
 /// stack.push(43, 0)?;
 /// assert_eq!(stack.pop(0)?, 43);
 /// # Ok::<(), aya::BpfError>(())
 /// ```
 #[doc(alias = "BPF_MAP_TYPE_STACK")]
-pub struct Stack<T: Deref<Target = Map>, V: Pod> {
+pub struct Stack<T, V: Pod> {
     inner: T,
     _v: PhantomData<V>,
 }
 
-impl<T: Deref<Target = Map>, V: Pod> Stack<T, V> {
-    fn new(map: T) -> Result<Stack<T, V>, MapError> {
-        let map_type = map.obj.map_type();
-        if map_type != BPF_MAP_TYPE_STACK as u32 {
-            return Err(MapError::InvalidMapType { map_type });
-        }
-        let expected = 0;
-        let size = map.obj.key_size() as usize;
-        if size != expected {
-            return Err(MapError::InvalidKeySize { size, expected });
-        }
+impl<T: AsRef<MapData>, V: Pod> Stack<T, V> {
+    pub(crate) fn new(map: T) -> Result<Stack<T, V>, MapError> {
+        let data = map.as_ref();
+        check_kv_size::<(), V>(data)?;
 
-        let expected = mem::size_of::<V>();
-        let size = map.obj.value_size() as usize;
-        if size != expected {
-            return Err(MapError::InvalidValueSize { size, expected });
-        }
-        let _fd = map.fd_or_err()?;
+        let _fd = data.fd_or_err()?;
 
         Ok(Stack {
             inner: map,
@@ -64,11 +50,11 @@ impl<T: Deref<Target = Map>, V: Pod> Stack<T, V> {
     ///
     /// This corresponds to the value of `bpf_map_def::max_entries` on the eBPF side.
     pub fn capacity(&self) -> u32 {
-        self.inner.obj.max_entries()
+        self.inner.as_ref().obj.max_entries()
     }
 }
 
-impl<T: Deref<Target = Map> + DerefMut<Target = Map>, V: Pod> Stack<T, V> {
+impl<T: AsMut<MapData>, V: Pod> Stack<T, V> {
     /// Removes the last element and returns it.
     ///
     /// # Errors
@@ -76,7 +62,7 @@ impl<T: Deref<Target = Map> + DerefMut<Target = Map>, V: Pod> Stack<T, V> {
     /// Returns [`MapError::ElementNotFound`] if the stack is empty, [`MapError::SyscallError`]
     /// if `bpf_map_lookup_and_delete_elem` fails.
     pub fn pop(&mut self, flags: u64) -> Result<V, MapError> {
-        let fd = self.inner.fd_or_err()?;
+        let fd = self.inner.as_mut().fd_or_err()?;
 
         let value = bpf_map_lookup_and_delete_elem::<u32, _>(fd, None, flags).map_err(
             |(_, io_error)| MapError::SyscallError {
@@ -93,7 +79,7 @@ impl<T: Deref<Target = Map> + DerefMut<Target = Map>, V: Pod> Stack<T, V> {
     ///
     /// [`MapError::SyscallError`] if `bpf_map_update_elem` fails.
     pub fn push(&mut self, value: V, flags: u64) -> Result<(), MapError> {
-        let fd = self.inner.fd_or_err()?;
+        let fd = self.inner.as_mut().fd_or_err()?;
         bpf_map_update_elem(fd, None::<&u32>, &value, flags).map_err(|(_, io_error)| {
             MapError::SyscallError {
                 call: "bpf_map_update_elem".to_owned(),
@@ -103,19 +89,3 @@ impl<T: Deref<Target = Map> + DerefMut<Target = Map>, V: Pod> Stack<T, V> {
         Ok(())
     }
 }
-
-impl<V: Pod> TryFrom<MapRef> for Stack<MapRef, V> {
-    type Error = MapError;
-
-    fn try_from(a: MapRef) -> Result<Stack<MapRef, V>, MapError> {
-        Stack::new(a)
-    }
-}
-
-impl<V: Pod> TryFrom<MapRefMut> for Stack<MapRefMut, V> {
-    type Error = MapError;
-
-    fn try_from(a: MapRefMut) -> Result<Stack<MapRefMut, V>, MapError> {
-        Stack::new(a)
-    }
-}
diff --git a/aya/src/maps/stack_trace.rs b/aya/src/maps/stack_trace.rs
index 0b7113f11..135387e40 100644
--- a/aya/src/maps/stack_trace.rs
+++ b/aya/src/maps/stack_trace.rs
@@ -1,11 +1,10 @@
 //! A hash map of kernel or user space stack traces.
 //!
 //! See [`StackTraceMap`] for documentation and examples.
-use std::{collections::BTreeMap, fs, io, mem, ops::Deref, path::Path, str::FromStr};
+use std::{collections::BTreeMap, convert::AsRef, fs, io, mem, path::Path, str::FromStr};
 
 use crate::{
-    generated::bpf_map_type::BPF_MAP_TYPE_STACK_TRACE,
-    maps::{IterableMap, Map, MapError, MapIter, MapKeys, MapRef, MapRefMut},
+    maps::{IterableMap, MapData, MapError, MapIter, MapKeys},
     sys::bpf_map_lookup_elem_ptr,
 };
 
@@ -36,7 +35,7 @@ use crate::{
 /// use aya::maps::StackTraceMap;
 /// use aya::util::kernel_symbols;
 ///
-/// let mut stack_traces = StackTraceMap::try_from(bpf.map("STACK_TRACES")?)?;
+/// let mut stack_traces = StackTraceMap::try_from(bpf.map("STACK_TRACES").unwrap())?;
 /// // load kernel symbols from /proc/kallsyms
 /// let ksyms = kernel_symbols()?;
 ///
@@ -68,14 +67,11 @@ pub struct StackTraceMap<T> {
     max_stack_depth: usize,
 }
 
-impl<T: Deref<Target = Map>> StackTraceMap<T> {
-    fn new(map: T) -> Result<StackTraceMap<T>, MapError> {
-        let map_type = map.obj.map_type();
-        if map_type != BPF_MAP_TYPE_STACK_TRACE as u32 {
-            return Err(MapError::InvalidMapType { map_type });
-        }
+impl<T: AsRef<MapData>> StackTraceMap<T> {
+    pub(crate) fn new(map: T) -> Result<StackTraceMap<T>, MapError> {
+        let data = map.as_ref();
         let expected = mem::size_of::<u32>();
-        let size = map.obj.key_size() as usize;
+        let size = data.obj.key_size() as usize;
         if size != expected {
             return Err(MapError::InvalidKeySize { size, expected });
         }
@@ -87,11 +83,11 @@ impl<T: Deref<Target = Map>> StackTraceMap<T> {
                     io_error,
                 }
             })?;
-        let size = map.obj.value_size() as usize;
+        let size = data.obj.value_size() as usize;
         if size > max_stack_depth * mem::size_of::<u64>() {
             return Err(MapError::InvalidValueSize { size, expected });
         }
-        let _fd = map.fd_or_err()?;
+        let _fd = data.fd_or_err()?;
 
         Ok(StackTraceMap {
             inner: map,
@@ -106,7 +102,7 @@ impl<T: Deref<Target = Map>> StackTraceMap<T> {
     /// Returns [`MapError::KeyNotFound`] if there is no stack trace with the
     /// given `stack_id`, or [`MapError::SyscallError`] if `bpf_map_lookup_elem` fails.
     pub fn get(&self, stack_id: &u32, flags: u64) -> Result<StackTrace, MapError> {
-        let fd = self.inner.fd_or_err()?;
+        let fd = self.inner.as_ref().fd_or_err()?;
 
         let mut frames = vec![0; self.max_stack_depth];
         bpf_map_lookup_elem_ptr(fd, Some(stack_id), frames.as_mut_ptr(), flags)
@@ -140,13 +136,13 @@ impl<T: Deref<Target = Map>> StackTraceMap<T> {
     /// An iterator visiting all the stack_ids in arbitrary order. The iterator element
     /// type is `Result<u32, MapError>`.
     pub fn stack_ids(&self) -> MapKeys<'_, u32> {
-        MapKeys::new(&self.inner)
+        MapKeys::new(self.inner.as_ref())
     }
 }
 
-impl<T: Deref<Target = Map>> IterableMap<u32, StackTrace> for StackTraceMap<T> {
-    fn map(&self) -> &Map {
-        &self.inner
+impl<T: AsRef<MapData>> IterableMap<u32, StackTrace> for StackTraceMap<T> {
+    fn map(&self) -> &MapData {
+        self.inner.as_ref()
     }
 
     fn get(&self, index: &u32) -> Result<StackTrace, MapError> {
@@ -154,23 +150,7 @@ impl<T: Deref<Target = Map>> IterableMap<u32, StackTrace> for StackTraceMap<T> {
     }
 }
 
-impl TryFrom<MapRef> for StackTraceMap<MapRef> {
-    type Error = MapError;
-
-    fn try_from(a: MapRef) -> Result<StackTraceMap<MapRef>, MapError> {
-        StackTraceMap::new(a)
-    }
-}
-
-impl TryFrom<MapRefMut> for StackTraceMap<MapRefMut> {
-    type Error = MapError;
-
-    fn try_from(a: MapRefMut) -> Result<StackTraceMap<MapRefMut>, MapError> {
-        StackTraceMap::new(a)
-    }
-}
-
-impl<'a, T: Deref<Target = Map>> IntoIterator for &'a StackTraceMap<T> {
+impl<'a, T: AsRef<MapData>> IntoIterator for &'a StackTraceMap<T> {
     type Item = Result<(u32, StackTrace), MapError>;
     type IntoIter = MapIter<'a, u32, StackTrace, StackTraceMap<T>>;
 
diff --git a/aya/src/obj/relocation.rs b/aya/src/obj/relocation.rs
index 4be758ff3..1564084a7 100644
--- a/aya/src/obj/relocation.rs
+++ b/aya/src/obj/relocation.rs
@@ -9,7 +9,7 @@ use crate::{
         bpf_insn, BPF_CALL, BPF_JMP, BPF_K, BPF_PSEUDO_CALL, BPF_PSEUDO_FUNC, BPF_PSEUDO_MAP_FD,
         BPF_PSEUDO_MAP_VALUE,
     },
-    maps::Map,
+    maps::MapData,
     obj::{Function, Object, Program},
     BpfError,
 };
@@ -62,7 +62,7 @@ pub(crate) struct Symbol {
 }
 
 impl Object {
-    pub fn relocate_maps(&mut self, maps: &HashMap<String, Map>) -> Result<(), BpfError> {
+    pub fn relocate_maps(&mut self, maps: &HashMap<String, MapData>) -> Result<(), BpfError> {
         let maps_by_section = maps
             .iter()
             .map(|(name, map)| (map.obj.section_index(), (name.as_str(), map)))
@@ -122,8 +122,8 @@ impl Object {
 fn relocate_maps<'a, I: Iterator<Item = &'a Relocation>>(
     fun: &mut Function,
     relocations: I,
-    maps_by_section: &HashMap<usize, (&str, &Map)>,
-    maps_by_symbol: &HashMap<usize, (&str, &Map)>,
+    maps_by_section: &HashMap<usize, (&str, &MapData)>,
+    maps_by_symbol: &HashMap<usize, (&str, &MapData)>,
     symbol_table: &HashMap<usize, Symbol>,
     text_section_index: Option<usize>,
 ) -> Result<(), RelocationError> {
@@ -438,6 +438,7 @@ fn insn_is_call(ins: &bpf_insn) -> bool {
 mod test {
     use crate::{
         bpf_map_def,
+        maps::MapData,
         obj::{self, BtfMap, LegacyMap, MapKind},
         BtfMapDef,
     };
@@ -460,8 +461,8 @@ mod test {
         unsafe { std::ptr::read_unaligned(bytes.as_ptr() as *const _) }
     }
 
-    fn fake_legacy_map(fd: i32, symbol_index: usize) -> Map {
-        Map {
+    fn fake_legacy_map(fd: i32, symbol_index: usize) -> MapData {
+        MapData {
             obj: obj::Map::Legacy(LegacyMap {
                 def: bpf_map_def {
                     ..Default::default()
@@ -477,8 +478,8 @@ mod test {
         }
     }
 
-    fn fake_btf_map(fd: i32, symbol_index: usize) -> Map {
-        Map {
+    fn fake_btf_map(fd: i32, symbol_index: usize) -> MapData {
+        MapData {
             obj: obj::Map::Btf(BtfMap {
                 def: BtfMapDef {
                     ..Default::default()
diff --git a/aya/src/programs/sk_msg.rs b/aya/src/programs/sk_msg.rs
index e801f9ff5..2cf4d51a3 100644
--- a/aya/src/programs/sk_msg.rs
+++ b/aya/src/programs/sk_msg.rs
@@ -1,7 +1,10 @@
 //! Skmsg programs.
+
+use std::os::unix::io::AsRawFd;
+
 use crate::{
     generated::{bpf_attach_type::BPF_SK_MSG_VERDICT, bpf_prog_type::BPF_PROG_TYPE_SK_MSG},
-    maps::sock::SocketMap,
+    maps::sock::SockMapFd,
     programs::{
         define_link_wrapper, load_program, ProgAttachLink, ProgAttachLinkId, ProgramData,
         ProgramError,
@@ -40,12 +43,16 @@ use crate::{
 /// use aya::maps::SockHash;
 /// use aya::programs::SkMsg;
 ///
-/// let mut intercept_egress = SockHash::try_from(bpf.map_mut("INTERCEPT_EGRESS")?)?;
+/// let intercept_egress: SockHash<_, u32> = bpf.map("INTERCEPT_EGRESS").unwrap().try_into()?;
+/// let map_fd = intercept_egress.fd()?;
+///
 /// let prog: &mut SkMsg = bpf.program_mut("intercept_egress_packet").unwrap().try_into()?;
 /// prog.load()?;
-/// prog.attach(&intercept_egress)?;
+/// prog.attach(map_fd)?;
 ///
 /// let mut client = TcpStream::connect("127.0.0.1:1234")?;
+/// let mut intercept_egress: SockHash<_, u32> = bpf.map_mut("INTERCEPT_EGRESS").unwrap().try_into()?;
+///
 /// intercept_egress.insert(1234, client.as_raw_fd(), 0)?;
 ///
 /// // the write will be intercepted
@@ -71,9 +78,9 @@ impl SkMsg {
     /// Attaches the program to the given sockmap.
     ///
     /// The returned value can be used to detach, see [SkMsg::detach].
-    pub fn attach(&mut self, map: &dyn SocketMap) -> Result<SkMsgLinkId, ProgramError> {
+    pub fn attach(&mut self, map: SockMapFd) -> Result<SkMsgLinkId, ProgramError> {
         let prog_fd = self.data.fd_or_err()?;
-        let map_fd = map.fd_or_err()?;
+        let map_fd = map.as_raw_fd();
 
         bpf_prog_attach(prog_fd, map_fd, BPF_SK_MSG_VERDICT).map_err(|(_, io_error)| {
             ProgramError::SyscallError {
diff --git a/aya/src/programs/sk_skb.rs b/aya/src/programs/sk_skb.rs
index f8766e4be..79e741fd0 100644
--- a/aya/src/programs/sk_skb.rs
+++ b/aya/src/programs/sk_skb.rs
@@ -1,10 +1,13 @@
 //! Skskb programs.
+
+use std::os::unix::io::AsRawFd;
+
 use crate::{
     generated::{
         bpf_attach_type::{BPF_SK_SKB_STREAM_PARSER, BPF_SK_SKB_STREAM_VERDICT},
         bpf_prog_type::BPF_PROG_TYPE_SK_SKB,
     },
-    maps::sock::SocketMap,
+    maps::sock::SockMapFd,
     programs::{
         define_link_wrapper, load_program, ProgAttachLink, ProgAttachLinkId, ProgramData,
         ProgramError,
@@ -38,10 +41,13 @@ pub enum SkSkbKind {
 /// use aya::maps::SockMap;
 /// use aya::programs::SkSkb;
 ///
-/// let intercept_ingress = SockMap::try_from(bpf.map_mut("INTERCEPT_INGRESS")?)?;
+/// let intercept_ingress: SockMap<_> = bpf.map("INTERCEPT_INGRESS").unwrap().try_into()?;
+/// let map_fd = intercept_ingress.fd()?;
+///
 /// let prog: &mut SkSkb = bpf.program_mut("intercept_ingress_packet").unwrap().try_into()?;
 /// prog.load()?;
-/// prog.attach(&intercept_ingress)?;
+/// prog.attach(map_fd)?;
+///
 /// # Ok::<(), aya::BpfError>(())
 /// ```
 ///
@@ -64,9 +70,9 @@ impl SkSkb {
     /// Attaches the program to the given socket map.
     ///
     /// The returned value can be used to detach, see [SkSkb::detach].
-    pub fn attach(&mut self, map: &dyn SocketMap) -> Result<SkSkbLinkId, ProgramError> {
+    pub fn attach(&mut self, map: SockMapFd) -> Result<SkSkbLinkId, ProgramError> {
         let prog_fd = self.data.fd_or_err()?;
-        let map_fd = map.fd_or_err()?;
+        let map_fd = map.as_raw_fd();
 
         let attach_type = match self.kind {
             SkSkbKind::StreamParser => BPF_SK_SKB_STREAM_PARSER,
diff --git a/xtask/src/docs/mod.rs b/xtask/src/docs/mod.rs
index 669852c6e..b6779cbaf 100644
--- a/xtask/src/docs/mod.rs
+++ b/xtask/src/docs/mod.rs
@@ -68,7 +68,7 @@ fn build_docs(working_dir: &PathBuf, abs_header_path: &Path) -> Result<(), anyho
         .env(
             "RUSTDOCFLAGS",
             format!(
-                "--cfg docsrs --html-in-header {}",
+                "--cfg docsrs --html-in-header {} -D warnings",
                 abs_header_path.to_str().unwrap()
             ),
         )


Test patch:
diff --git a/test/integration-test/src/tests/load.rs b/test/integration-test/src/tests/load.rs
index 1dbffd795..de01e6c2b 100644
--- a/test/integration-test/src/tests/load.rs
+++ b/test/integration-test/src/tests/load.rs
@@ -2,7 +2,7 @@ use std::{process::Command, thread, time};
 
 use aya::{
     include_bytes_aligned,
-    maps::{Array, MapRefMut},
+    maps::Array,
     programs::{
         links::{FdLink, PinnedLink},
         TracePoint, Xdp, XdpFlags,
@@ -36,8 +36,8 @@ fn multiple_btf_maps() -> anyhow::Result<()> {
         include_bytes_aligned!("../../../../target/bpfel-unknown-none/debug/multimap-btf.bpf.o");
     let mut bpf = Bpf::load(bytes)?;
 
-    let map_1: Array<MapRefMut, u64> = Array::try_from(bpf.map_mut("map_1")?)?;
-    let map_2: Array<MapRefMut, u64> = Array::try_from(bpf.map_mut("map_2")?)?;
+    let map_1: Array<_, u64> = bpf.take_map("map_1").unwrap().try_into()?;
+    let map_2: Array<_, u64> = bpf.take_map("map_2").unwrap().try_into()?;
 
     let prog: &mut TracePoint = bpf.program_mut("tracepoint").unwrap().try_into().unwrap();
     prog.load().unwrap();