diff --git a/bindings/rust/build.rs b/bindings/rust/build.rs
index 1f5e225153..7e4d77e4fd 100644
--- a/bindings/rust/build.rs
+++ b/bindings/rust/build.rs
@@ -1,4 +1,3 @@
-use pkg_config;
 use std::env;
 use std::path::PathBuf;
 use std::process::Command;
diff --git a/bindings/rust/src/ffi.rs b/bindings/rust/src/ffi.rs
index a4624a02c4..642be8b5d2 100644
--- a/bindings/rust/src/ffi.rs
+++ b/bindings/rust/src/ffi.rs
@@ -88,14 +88,21 @@ extern "C" {
 }
 
 pub struct UcHook<'a, D: 'a, F: 'a> {
+    pub unicorn: *mut Unicorn<'a, D>,
     pub callback: F,
-    pub uc: Unicorn<'a, D>,
 }
 
 pub trait IsUcHook<'a> {}
 
 impl<'a, D, F> IsUcHook<'a> for UcHook<'a, D, F> {}
 
+macro_rules! destructure_hook {
+    ($hook:ident) => {{
+        let UcHook { unicorn, callback } = unsafe { &mut *$hook };
+        (unsafe { &mut **unicorn }, callback)
+    }};
+}
+
 pub extern "C" fn mmio_read_callback_proxy<D, F>(
     uc: uc_handle,
     offset: u64,
@@ -103,11 +110,11 @@ pub extern "C" fn mmio_read_callback_proxy<D, F>(
     user_data: *mut UcHook<D, F>,
 ) -> u64
 where
-    F: FnMut(&mut crate::Unicorn<D>, u64, usize) -> u64,
+    F: FnMut(&mut Unicorn<D>, u64, usize) -> u64,
 {
-    let user_data = unsafe { &mut *user_data };
-    debug_assert_eq!(uc, user_data.uc.get_handle());
-    (user_data.callback)(&mut user_data.uc, offset, size)
+    let (unicorn, callback) = destructure_hook!(user_data);
+    debug_assert_eq!(uc, unicorn.get_handle());
+    callback(unicorn, offset, size)
 }
 
 pub extern "C" fn mmio_write_callback_proxy<D, F>(
@@ -117,11 +124,11 @@ pub extern "C" fn mmio_write_callback_proxy<D, F>(
     value: u64,
     user_data: *mut UcHook<D, F>,
 ) where
-    F: FnMut(&mut crate::Unicorn<D>, u64, usize, u64),
+    F: FnMut(&mut Unicorn<D>, u64, usize, u64),
 {
-    let user_data = unsafe { &mut *user_data };
-    debug_assert_eq!(uc, user_data.uc.get_handle());
-    (user_data.callback)(&mut user_data.uc, offset, size, value);
+    let (unicorn, callback) = destructure_hook!(user_data);
+    debug_assert_eq!(uc, unicorn.get_handle());
+    callback(unicorn, offset, size, value);
 }
 
 pub extern "C" fn code_hook_proxy<D, F>(
@@ -130,11 +137,11 @@ pub extern "C" fn code_hook_proxy<D, F>(
     size: u32,
     user_data: *mut UcHook<D, F>,
 ) where
-    F: FnMut(&mut crate::Unicorn<D>, u64, u32),
+    F: FnMut(&mut Unicorn<D>, u64, u32),
 {
-    let user_data = unsafe { &mut *user_data };
-    debug_assert_eq!(uc, user_data.uc.get_handle());
-    (user_data.callback)(&mut user_data.uc, address, size);
+    let (unicorn, callback) = destructure_hook!(user_data);
+    debug_assert_eq!(uc, unicorn.get_handle());
+    callback(unicorn, address, size);
 }
 
 pub extern "C" fn block_hook_proxy<D, F>(
@@ -143,11 +150,11 @@ pub extern "C" fn block_hook_proxy<D, F>(
     size: u32,
     user_data: *mut UcHook<D, F>,
 ) where
-    F: FnMut(&mut crate::Unicorn<D>, u64, u32),
+    F: FnMut(&mut Unicorn<D>, u64, u32),
 {
-    let user_data = unsafe { &mut *user_data };
-    debug_assert_eq!(uc, user_data.uc.get_handle());
-    (user_data.callback)(&mut user_data.uc, address, size);
+    let (unicorn, callback) = destructure_hook!(user_data);
+    debug_assert_eq!(uc, unicorn.get_handle());
+    callback(unicorn, address, size);
 }
 
 pub extern "C" fn mem_hook_proxy<D, F>(
@@ -159,20 +166,20 @@ pub extern "C" fn mem_hook_proxy<D, F>(
     user_data: *mut UcHook<D, F>,
 ) -> bool
 where
-    F: FnMut(&mut crate::Unicorn<D>, MemType, u64, usize, i64) -> bool,
+    F: FnMut(&mut Unicorn<D>, MemType, u64, usize, i64) -> bool,
 {
-    let user_data = unsafe { &mut *user_data };
-    debug_assert_eq!(uc, user_data.uc.get_handle());
-    (user_data.callback)(&mut user_data.uc, mem_type, address, size as usize, value)
+    let (unicorn, callback) = destructure_hook!(user_data);
+    debug_assert_eq!(uc, unicorn.get_handle());
+    callback(unicorn, mem_type, address, size as usize, value)
 }
 
 pub extern "C" fn intr_hook_proxy<D, F>(uc: uc_handle, value: u32, user_data: *mut UcHook<D, F>)
 where
-    F: FnMut(&mut crate::Unicorn<D>, u32),
+    F: FnMut(&mut Unicorn<D>, u32),
 {
-    let user_data = unsafe { &mut *user_data };
-    debug_assert_eq!(uc, user_data.uc.get_handle());
-    (user_data.callback)(&mut user_data.uc, value);
+    let (unicorn, callback) = destructure_hook!(user_data);
+    debug_assert_eq!(uc, unicorn.get_handle());
+    callback(unicorn, value);
 }
 
 pub extern "C" fn insn_in_hook_proxy<D, F>(
@@ -181,20 +188,20 @@ pub extern "C" fn insn_in_hook_proxy<D, F>(
     size: usize,
     user_data: *mut UcHook<D, F>,
 ) where
-    F: FnMut(&mut crate::Unicorn<D>, u32, usize) -> u32,
+    F: FnMut(&mut Unicorn<D>, u32, usize) -> u32,
 {
-    let user_data = unsafe { &mut *user_data };
-    debug_assert_eq!(uc, user_data.uc.get_handle());
-    (user_data.callback)(&mut user_data.uc, port, size);
+    let (unicorn, callback) = destructure_hook!(user_data);
+    debug_assert_eq!(uc, unicorn.get_handle());
+    callback(unicorn, port, size);
 }
 
 pub extern "C" fn insn_invalid_hook_proxy<D, F>(uc: uc_handle, user_data: *mut UcHook<D, F>) -> bool
 where
-    F: FnMut(&mut crate::Unicorn<D>) -> bool,
+    F: FnMut(&mut Unicorn<D>) -> bool,
 {
-    let user_data = unsafe { &mut *user_data };
-    debug_assert_eq!(uc, user_data.uc.get_handle());
-    (user_data.callback)(&mut user_data.uc)
+    let (unicorn, callback) = destructure_hook!(user_data);
+    debug_assert_eq!(uc, unicorn.get_handle());
+    callback(unicorn)
 }
 
 pub extern "C" fn insn_out_hook_proxy<D, F>(
@@ -204,18 +211,18 @@ pub extern "C" fn insn_out_hook_proxy<D, F>(
     value: u32,
     user_data: *mut UcHook<D, F>,
 ) where
-    F: FnMut(&mut crate::Unicorn<D>, u32, usize, u32),
+    F: FnMut(&mut Unicorn<D>, u32, usize, u32),
 {
-    let user_data = unsafe { &mut *user_data };
-    debug_assert_eq!(uc, user_data.uc.get_handle());
-    (user_data.callback)(&mut user_data.uc, port, size, value);
+    let (unicorn, callback) = destructure_hook!(user_data);
+    debug_assert_eq!(uc, unicorn.get_handle());
+    callback(unicorn, port, size, value);
 }
 
 pub extern "C" fn insn_sys_hook_proxy<D, F>(uc: uc_handle, user_data: *mut UcHook<D, F>)
 where
-    F: FnMut(&mut crate::Unicorn<D>),
+    F: FnMut(&mut Unicorn<D>),
 {
-    let user_data = unsafe { &mut *user_data };
-    debug_assert_eq!(uc, user_data.uc.get_handle());
-    (user_data.callback)(&mut user_data.uc);
+    let (unicorn, callback) = destructure_hook!(user_data);
+    debug_assert_eq!(uc, unicorn.get_handle());
+    callback(unicorn);
 }
diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs
index 7959e2d5de..26a0841723 100644
--- a/bindings/rust/src/lib.rs
+++ b/bindings/rust/src/lib.rs
@@ -50,8 +50,8 @@ pub use crate::{
     unicorn_const::*, x86::*,
 };
 
-use alloc::{boxed::Box, rc::Rc, vec::Vec};
-use core::{cell::UnsafeCell, ptr};
+use alloc::{boxed::Box, vec::Vec};
+use core::ptr;
 use ffi::uc_handle;
 use libc::c_void;
 use unicorn_const::{uc_error, Arch, HookType, MemRegion, MemType, Mode, Permission, Query};
@@ -129,19 +129,20 @@ impl<'a> MmioCallbackScope<'a> {
     }
 }
 
-pub struct UnicornInner<'a, D> {
-    pub handle: uc_handle,
-    pub ffi: bool,
-    pub arch: Arch,
+/// A Unicorn emulator instance.
+pub struct Unicorn<'a, D: 'a> {
+    handle: uc_handle,
+    ffi: bool,
+    arch: Arch,
     /// to keep ownership over the hook for this uc instance's lifetime
-    pub hooks: Vec<(ffi::uc_hook, Box<dyn ffi::IsUcHook<'a> + 'a>)>,
+    hooks: Vec<(ffi::uc_hook, Box<dyn ffi::IsUcHook<'a> + 'a>)>,
     /// To keep ownership over the mmio callbacks for this uc instance's lifetime
-    pub mmio_callbacks: Vec<MmioCallbackScope<'a>>,
-    pub data: D,
+    mmio_callbacks: Vec<MmioCallbackScope<'a>>,
+    data: D,
 }
 
 /// Drop UC
-impl<'a, D> Drop for UnicornInner<'a, D> {
+impl<'a, D: 'a> Drop for Unicorn<'a, D> {
     fn drop(&mut self) {
         if !self.ffi && !self.handle.is_null() {
             unsafe { ffi::uc_close(self.handle) };
@@ -150,11 +151,6 @@ impl<'a, D> Drop for UnicornInner<'a, D> {
     }
 }
 
-/// A Unicorn emulator instance.
-pub struct Unicorn<'a, D: 'a> {
-    inner: Rc<UnsafeCell<UnicornInner<'a, D>>>,
-}
-
 impl<'a> Unicorn<'a, ()> {
     /// Create a new instance of the unicorn engine for the specified architecture
     /// and hardware mode.
@@ -166,6 +162,7 @@ impl<'a> Unicorn<'a, ()> {
 impl<'a> TryFrom<uc_handle> for Unicorn<'a, ()> {
     type Error = uc_error;
 
+    #[allow(clippy::not_unsafe_ptr_arg_deref)]
     fn try_from(handle: uc_handle) -> Result<Unicorn<'a, ()>, uc_error> {
         if handle.is_null() {
             return Err(uc_error::HANDLE);
@@ -176,22 +173,17 @@ impl<'a> TryFrom<uc_handle> for Unicorn<'a, ()> {
             return Err(err);
         }
         Ok(Unicorn {
-            inner: Rc::new(UnsafeCell::from(UnicornInner {
-                handle,
-                ffi: true,
-                arch: arch.try_into()?,
-                data: (),
-                hooks: vec![],
-                mmio_callbacks: vec![],
-            })),
+            handle,
+            ffi: true,
+            arch: arch.try_into()?,
+            data: (),
+            hooks: vec![],
+            mmio_callbacks: vec![],
         })
     }
 }
 
-impl<'a, D> Unicorn<'a, D>
-where
-    D: 'a,
-{
+impl<'a, D: 'a> Unicorn<'a, D> {
     /// Create a new instance of the unicorn engine for the specified architecture
     /// and hardware mode.
     pub fn new_with_data(arch: Arch, mode: Mode, data: D) -> Result<Unicorn<'a, D>, uc_error> {
@@ -199,14 +191,12 @@ where
         let err = unsafe { ffi::uc_open(arch, mode, &mut handle) };
         if err == uc_error::OK {
             Ok(Unicorn {
-                inner: Rc::new(UnsafeCell::from(UnicornInner {
-                    handle,
-                    ffi: false,
-                    arch,
-                    data,
-                    hooks: vec![],
-                    mmio_callbacks: vec![],
-                })),
+                handle,
+                ffi: false,
+                arch,
+                data,
+                hooks: vec![],
+                mmio_callbacks: vec![],
             })
         } else {
             Err(err)
@@ -214,46 +204,38 @@ where
     }
 }
 
-impl<'a, D> core::fmt::Debug for Unicorn<'a, D> {
+impl<'a, D: 'a> core::fmt::Debug for Unicorn<'a, D> {
     fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
         write!(formatter, "Unicorn {{ uc: {:p} }}", self.get_handle())
     }
 }
 
-impl<'a, D> Unicorn<'a, D> {
-    fn inner(&self) -> &UnicornInner<'a, D> {
-        unsafe { self.inner.get().as_ref().unwrap() }
-    }
-
-    fn inner_mut(&mut self) -> &mut UnicornInner<'a, D> {
-        unsafe { self.inner.get().as_mut().unwrap() }
-    }
-
+impl<'a, D: 'a> Unicorn<'a, D> {
     /// Return whatever data was passed during initialization.
     ///
     /// For an example, have a look at `utils::init_emu_with_heap` where
     /// a struct is passed which is used for a custom allocator.
     #[must_use]
     pub fn get_data(&self) -> &D {
-        &self.inner().data
+        &self.data
     }
 
     /// Return a mutable reference to whatever data was passed during initialization.
     #[must_use]
     pub fn get_data_mut(&mut self) -> &mut D {
-        &mut self.inner_mut().data
+        &mut self.data
     }
 
     /// Return the architecture of the current emulator.
     #[must_use]
     pub fn get_arch(&self) -> Arch {
-        self.inner().arch
+        self.arch
     }
 
     /// Return the handle of the current emulator.
     #[must_use]
     pub fn get_handle(&self) -> uc_handle {
-        self.inner().handle
+        self.handle
     }
 
     /// Returns a vector with the memory regions that are mapped in the emulator.
@@ -369,17 +351,13 @@ impl<'a, D> Unicorn<'a, D> {
         let mut read_data = read_callback.map(|c| {
             Box::new(ffi::UcHook {
                 callback: c,
-                uc: Unicorn {
-                    inner: self.inner.clone(),
-                },
+                unicorn: self,
             })
         });
         let mut write_data = write_callback.map(|c| {
             Box::new(ffi::UcHook {
                 callback: c,
-                uc: Unicorn {
-                    inner: self.inner.clone(),
-                },
+                unicorn: self,
             })
         });
 
@@ -404,7 +382,7 @@ impl<'a, D> Unicorn<'a, D> {
         if err == uc_error::OK {
             let rd = read_data.map(|c| c as Box<dyn ffi::IsUcHook>);
             let wd = write_data.map(|c| c as Box<dyn ffi::IsUcHook>);
-            self.inner_mut().mmio_callbacks.push(MmioCallbackScope {
+            self.mmio_callbacks.push(MmioCallbackScope {
                 regions: vec![(address, size)],
                 read_callback: rd,
                 write_callback: wd,
@@ -475,12 +453,10 @@ impl<'a, D> Unicorn<'a, D> {
     }
 
     fn mmio_unmap(&mut self, address: u64, size: libc::size_t) {
-        for scope in self.inner_mut().mmio_callbacks.iter_mut() {
+        for scope in self.mmio_callbacks.iter_mut() {
             scope.unmap(address, size);
         }
-        self.inner_mut()
-            .mmio_callbacks
-            .retain(|scope| scope.has_regions());
+        self.mmio_callbacks.retain(|scope| scope.has_regions());
     }
 
     /// Set the memory permissions for an existing memory region.
@@ -586,7 +562,8 @@ impl<'a, D> Unicorn<'a, D> {
             return Err(uc_error::ARCH);
         }
 
-        let err: uc_error = unsafe { ffi::uc_reg_read(self.get_handle(), curr_reg_id, value.as_mut_ptr() as _) };
+        let err: uc_error =
+            unsafe { ffi::uc_reg_read(self.get_handle(), curr_reg_id, value.as_mut_ptr() as _) };
 
         if err == uc_error::OK {
             boxed = value.into_boxed_slice();
@@ -617,14 +594,12 @@ impl<'a, D> Unicorn<'a, D> {
         callback: F,
     ) -> Result<ffi::uc_hook, uc_error>
     where
-        F: FnMut(&mut crate::Unicorn<D>, u64, u32) + 'a,
+        F: FnMut(&mut Unicorn<D>, u64, u32) + 'a,
     {
         let mut hook_ptr = core::ptr::null_mut();
         let mut user_data = Box::new(ffi::UcHook {
             callback,
-            uc: Unicorn {
-                inner: self.inner.clone(),
-            },
+            unicorn: self,
         });
 
         let err = unsafe {
@@ -639,7 +614,7 @@ impl<'a, D> Unicorn<'a, D> {
             )
         };
         if err == uc_error::OK {
-            self.inner_mut().hooks.push((hook_ptr, user_data));
+            self.hooks.push((hook_ptr, user_data));
             Ok(hook_ptr)
         } else {
             Err(err)
@@ -654,9 +629,7 @@ impl<'a, D> Unicorn<'a, D> {
         let mut hook_ptr = core::ptr::null_mut();
         let mut user_data = Box::new(ffi::UcHook {
             callback,
-            uc: Unicorn {
-                inner: self.inner.clone(),
-            },
+            unicorn: self,
         });
 
         let err = unsafe {
@@ -671,8 +644,7 @@ impl<'a, D> Unicorn<'a, D> {
             )
         };
         if err == uc_error::OK {
-            self.inner_mut().hooks.push((hook_ptr, user_data));
-
+            self.hooks.push((hook_ptr, user_data));
             Ok(hook_ptr)
         } else {
             Err(err)
@@ -697,9 +669,7 @@ impl<'a, D> Unicorn<'a, D> {
         let mut hook_ptr = core::ptr::null_mut();
         let mut user_data = Box::new(ffi::UcHook {
             callback,
-            uc: Unicorn {
-                inner: self.inner.clone(),
-            },
+            unicorn: self,
         });
 
         let err = unsafe {
@@ -714,8 +684,7 @@ impl<'a, D> Unicorn<'a, D> {
             )
         };
         if err == uc_error::OK {
-            self.inner_mut().hooks.push((hook_ptr, user_data));
-
+            self.hooks.push((hook_ptr, user_data));
             Ok(hook_ptr)
         } else {
             Err(err)
@@ -730,9 +699,7 @@ impl<'a, D> Unicorn<'a, D> {
         let mut hook_ptr = core::ptr::null_mut();
         let mut user_data = Box::new(ffi::UcHook {
             callback,
-            uc: Unicorn {
-                inner: self.inner.clone(),
-            },
+            unicorn: self,
         });
 
         let err = unsafe {
@@ -747,8 +714,7 @@ impl<'a, D> Unicorn<'a, D> {
             )
         };
         if err == uc_error::OK {
-            self.inner_mut().hooks.push((hook_ptr, user_data));
-
+            self.hooks.push((hook_ptr, user_data));
             Ok(hook_ptr)
         } else {
             Err(err)
@@ -763,9 +729,7 @@ impl<'a, D> Unicorn<'a, D> {
         let mut hook_ptr = core::ptr::null_mut();
         let mut user_data = Box::new(ffi::UcHook {
             callback,
-            uc: Unicorn {
-                inner: self.inner.clone(),
-            },
+            unicorn: self,
         });
 
         let err = unsafe {
@@ -780,8 +744,7 @@ impl<'a, D> Unicorn<'a, D> {
             )
         };
         if err == uc_error::OK {
-            self.inner_mut().hooks.push((hook_ptr, user_data));
-
+            self.hooks.push((hook_ptr, user_data));
             Ok(hook_ptr)
         } else {
             Err(err)
@@ -796,9 +759,7 @@ impl<'a, D> Unicorn<'a, D> {
         let mut hook_ptr = core::ptr::null_mut();
         let mut user_data = Box::new(ffi::UcHook {
             callback,
-            uc: Unicorn {
-                inner: self.inner.clone(),
-            },
+            unicorn: self,
         });
 
         let err = unsafe {
@@ -814,8 +775,7 @@ impl<'a, D> Unicorn<'a, D> {
             )
         };
         if err == uc_error::OK {
-            self.inner_mut().hooks.push((hook_ptr, user_data));
-
+            self.hooks.push((hook_ptr, user_data));
             Ok(hook_ptr)
         } else {
             Err(err)
@@ -830,9 +790,7 @@ impl<'a, D> Unicorn<'a, D> {
         let mut hook_ptr = core::ptr::null_mut();
         let mut user_data = Box::new(ffi::UcHook {
             callback,
-            uc: Unicorn {
-                inner: self.inner.clone(),
-            },
+            unicorn: self,
         });
 
         let err = unsafe {
@@ -848,8 +806,7 @@ impl<'a, D> Unicorn<'a, D> {
             )
         };
         if err == uc_error::OK {
-            self.inner_mut().hooks.push((hook_ptr, user_data));
-
+            self.hooks.push((hook_ptr, user_data));
             Ok(hook_ptr)
         } else {
             Err(err)
@@ -870,9 +827,7 @@ impl<'a, D> Unicorn<'a, D> {
         let mut hook_ptr = core::ptr::null_mut();
         let mut user_data = Box::new(ffi::UcHook {
             callback,
-            uc: Unicorn {
-                inner: self.inner.clone(),
-            },
+            unicorn: self,
         });
 
         let err = unsafe {
@@ -888,8 +843,7 @@ impl<'a, D> Unicorn<'a, D> {
             )
         };
         if err == uc_error::OK {
-            self.inner_mut().hooks.push((hook_ptr, user_data));
-
+            self.hooks.push((hook_ptr, user_data));
             Ok(hook_ptr)
         } else {
             Err(err)
@@ -899,14 +853,13 @@ impl<'a, D> Unicorn<'a, D> {
     /// Remove a hook.
     ///
     /// `hook` is the value returned by `add_*_hook` functions.
+    #[allow(clippy::not_unsafe_ptr_arg_deref)]
     pub fn remove_hook(&mut self, hook: ffi::uc_hook) -> Result<(), uc_error> {
         // drop the hook
-        let inner = self.inner_mut();
-        inner
-            .hooks
+        self.hooks
             .retain(|(hook_ptr, _hook_impl)| hook_ptr != &hook);
 
-        let err: uc_error = unsafe { ffi::uc_hook_del(inner.handle, hook) };
+        let err: uc_error = unsafe { ffi::uc_hook_del(self.get_handle(), hook) };
 
         if err == uc_error::OK {
             Ok(())