diff --git a/book/src/bridge/attributes.md b/book/src/bridge/attributes.md
index 4063c826a..72e72af58 100644
--- a/book/src/bridge/attributes.md
+++ b/book/src/bridge/attributes.md
@@ -43,3 +43,8 @@ For [`#[qproperty]`](./extern_rustqt.md#properties), a CXX or Rust name can be p
 The `#[auto_cxx_name]` and `#[auto_rust_name]` attributes can be used to automatically rename cxx and rust names.
 These are placed at a block level on `extern "RustQt"` or `extern "C++Qt"` blocks, and will automatically case convert the items inside, unless they specify either a `rust_name` or `cxx_name`.
 By default `#[auto_cxx_name]` will generate a camelCase conversion for`cxx_name` and `#[auto_rust_name]` will generate a snake_case conversion for `rust_name`.
+
+### Automatic wrapping
+
+A fairly common operation is calling a method on the inner rust type, via the `.rust()` accessor. This can be simplified
+with the `#[auto_wrap]` attribute. This will generate a wrapper for your function which accesses the rust method of that name.
diff --git a/crates/cxx-qt-gen/src/generator/rust/method.rs b/crates/cxx-qt-gen/src/generator/rust/method.rs
index c251f16a8..559c67d5c 100644
--- a/crates/cxx-qt-gen/src/generator/rust/method.rs
+++ b/crates/cxx-qt-gen/src/generator/rust/method.rs
@@ -3,7 +3,7 @@
 //
 // SPDX-License-Identifier: MIT OR Apache-2.0
 
-use crate::generator::rust::get_params_tokens;
+use crate::generator::rust::{get_call_params_tokens, get_params_tokens};
 use crate::{
     generator::{naming::qobject::QObjectNames, rust::fragment::GeneratedRustFragment},
     parser::method::ParsedMethod,
@@ -31,6 +31,8 @@ pub fn generate_rust_methods(
                 cpp_class_name_rust,
             );
 
+            let call_parameters = get_call_params_tokens(&invokable.parameters);
+
             let return_type = &invokable.method.sig.output;
 
             let cfgs = &invokable.cfgs;
@@ -54,24 +56,38 @@ pub fn generate_rust_methods(
                 Some(quote! { unsafe })
             };
 
-            GeneratedRustFragment::from_cxx_item(parse_quote_spanned! {
-                invokable.method.span() =>
-                // Note: extern "Rust" block does not need to be unsafe
-                #block_safety extern #block_type {
-                    // Note that we are exposing a Rust method on the C++ type to C++
-                    //
-                    // CXX ends up generating the source, then we generate the matching header.
-                    #[cxx_name = #invokable_ident_cpp]
-                    // Needed for QObjects to have a namespace on their type or extern block
-                    //
-                    // A Namespace from cxx_qt::bridge would be automatically applied to all children
-                    // but to apply it to only certain types, it is needed here too
-                    #cxx_namespace
-                    #(#cfgs)*
-                    #[doc(hidden)]
-                    #unsafe_call fn #invokable_ident_rust(#parameter_signatures) #return_type;
-                }
-            })
+            let wrapper_fn = if invokable.wrap {
+                vec![parse_quote_spanned! {
+                    invokable.method.span() =>
+                    #unsafe_call fn #invokable_ident_rust(#parameter_signatures) #return_type {
+                        self.rust().#invokable_ident_rust(#call_parameters)
+                    }
+                }]
+            } else {
+                vec![]
+            };
+
+            GeneratedRustFragment {
+                cxx_mod_contents: vec![parse_quote_spanned! {
+                    invokable.method.span() =>
+                    // Note: extern "Rust" block does not need to be unsafe
+                    #block_safety extern #block_type {
+                        // Note that we are exposing a Rust method on the C++ type to C++
+                        //
+                        // CXX ends up generating the source, then we generate the matching header.
+                        #[cxx_name = #invokable_ident_cpp]
+                        // Needed for QObjects to have a namespace on their type or extern block
+                        //
+                        // A Namespace from cxx_qt::bridge would be automatically applied to all children
+                        // but to apply it to only certain types, it is needed here too
+                        #cxx_namespace
+                        #(#cfgs)*
+                        #[doc(hidden)]
+                        #unsafe_call fn #invokable_ident_rust(#parameter_signatures) #return_type;
+                    }
+                }],
+                cxx_qt_mod_contents: wrapper_fn,
+            }
         })
         .collect::<Vec<_>>();
 
@@ -98,10 +114,12 @@ mod tests {
         };
         let method3: ForeignItemFn = parse_quote! {
             #[cxx_name = "opaqueInvokable"]
+            #[auto_wrap]
             fn opaque_invokable(self: Pin<&mut MyObject>, param: &QColor) -> UniquePtr<QColor>;
         };
         let method4: ForeignItemFn = parse_quote! {
             #[cxx_name = "unsafeInvokable"]
+            #[auto_wrap]
             unsafe fn unsafe_invokable(self: &MyObject, param: *mut T) -> *mut T;
         };
         let invokables = vec![
@@ -116,7 +134,7 @@ mod tests {
             generate_rust_methods(&invokables.iter().collect::<Vec<_>>(), &qobject_names).unwrap();
 
         assert_eq!(generated.cxx_mod_contents.len(), 4);
-        assert_eq!(generated.cxx_qt_mod_contents.len(), 0);
+        assert_eq!(generated.cxx_qt_mod_contents.len(), 2);
 
         // void_invokable
         assert_tokens_eq(
@@ -154,6 +172,15 @@ mod tests {
             },
         );
 
+        assert_tokens_eq(
+            &generated.cxx_qt_mod_contents[0],
+            quote! {
+                fn opaque_invokable(self: Pin<&mut MyObject>, param: &QColor) -> UniquePtr<QColor> {
+                    self.rust().opaque_invokable(param)
+                }
+            },
+        );
+
         // unsafe_invokable
         assert_tokens_eq(
             &generated.cxx_mod_contents[3],
@@ -165,5 +192,14 @@ mod tests {
                 }
             },
         );
+
+        assert_tokens_eq(
+            &generated.cxx_qt_mod_contents[1],
+            quote! {
+                unsafe fn unsafe_invokable(self:&MyObject, param: *mut T) -> *mut T {
+                    self.rust().unsafe_invokable(param)
+                }
+            },
+        );
     }
 }
diff --git a/crates/cxx-qt-gen/src/generator/rust/mod.rs b/crates/cxx-qt-gen/src/generator/rust/mod.rs
index acd94e05d..301503768 100644
--- a/crates/cxx-qt-gen/src/generator/rust/mod.rs
+++ b/crates/cxx-qt-gen/src/generator/rust/mod.rs
@@ -146,6 +146,22 @@ pub fn get_params_tokens(
     }
 }
 
+/// Return the [TokenStream] of the parsed parameters, which would be used to call the fn, for use in generation
+pub fn get_call_params_tokens(parameters: &[ParsedFunctionParameter]) -> TokenStream {
+    if parameters.is_empty() {
+        quote! {}
+    } else {
+        let parameters = parameters
+            .iter()
+            .map(|parameter| {
+                let ident = &parameter.ident;
+                quote! { #ident }
+            })
+            .collect::<Vec<TokenStream>>();
+        quote! { #(#parameters),* }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/crates/cxx-qt-gen/src/parser/method.rs b/crates/cxx-qt-gen/src/parser/method.rs
index 89c708da5..3dcf2b28f 100644
--- a/crates/cxx-qt-gen/src/parser/method.rs
+++ b/crates/cxx-qt-gen/src/parser/method.rs
@@ -59,6 +59,8 @@ pub struct ParsedMethod {
     pub is_qinvokable: bool,
     /// Whether the method is a pure virtual method
     pub is_pure: bool,
+    /// Whether to auto generate a wrapper for this method outside the bridge
+    pub wrap: bool,
     // No docs field since the docs should be on the method implementation outside the bridge
     // This means any docs on the bridge declaration would be ignored
     /// Cfgs for the method
@@ -68,7 +70,7 @@ pub struct ParsedMethod {
 }
 
 impl ParsedMethod {
-    const ALLOWED_ATTRS: [&'static str; 9] = [
+    const ALLOWED_ATTRS: [&'static str; 10] = [
         "cxx_name",
         "rust_name",
         "qinvokable",
@@ -76,6 +78,7 @@ impl ParsedMethod {
         "cxx_override",
         "cxx_virtual",
         "cxx_pure",
+        "auto_wrap",
         "doc",
         "cfg",
     ];
@@ -127,6 +130,7 @@ impl ParsedMethod {
         // Determine if the method is invokable
         let is_qinvokable = attrs.contains_key("qinvokable");
         let is_pure = attrs.contains_key("cxx_pure");
+        let wrap = attrs.contains_key("auto_wrap");
         let specifiers = ParsedQInvokableSpecifiers::from_attrs(attrs);
 
         Ok(Self {
@@ -134,6 +138,7 @@ impl ParsedMethod {
             specifiers,
             is_qinvokable,
             is_pure,
+            wrap,
             cfgs,
             unsafe_block,
         })
diff --git a/crates/cxx-qt-gen/test_inputs/passthrough_and_naming.rs b/crates/cxx-qt-gen/test_inputs/passthrough_and_naming.rs
index bb4fa2c2e..fb0dd1108 100644
--- a/crates/cxx-qt-gen/test_inputs/passthrough_and_naming.rs
+++ b/crates/cxx-qt-gen/test_inputs/passthrough_and_naming.rs
@@ -142,7 +142,8 @@ pub mod ffi {
         fn invokable_name(self: Pin<&mut SecondObject>);
 
         #[cxx_name = "myRenamedFunction"]
-        fn my_function(self: &SecondObject);
+        #[auto_wrap]
+        fn my_function(self: &SecondObject, param: i32);
     }
 
     extern "RustQt" {
diff --git a/crates/cxx-qt-gen/test_outputs/passthrough_and_naming.h b/crates/cxx-qt-gen/test_outputs/passthrough_and_naming.h
index 76dafdad2..a3cfd34be 100644
--- a/crates/cxx-qt-gen/test_outputs/passthrough_and_naming.h
+++ b/crates/cxx-qt-gen/test_outputs/passthrough_and_naming.h
@@ -165,7 +165,7 @@ class SecondObject
   Q_SLOT void setPropertyName(::std::int32_t value) noexcept;
   Q_SIGNAL void propertyNameChanged();
   Q_INVOKABLE void invokableName() noexcept;
-  void myRenamedFunction() const noexcept;
+  void myRenamedFunction(::std::int32_t param) const noexcept;
   Q_SIGNAL void ready();
   explicit SecondObject(QObject* parent = nullptr);
 };
diff --git a/crates/cxx-qt-gen/test_outputs/passthrough_and_naming.rs b/crates/cxx-qt-gen/test_outputs/passthrough_and_naming.rs
index 5e0f5b3a7..dcfcf37a5 100644
--- a/crates/cxx-qt-gen/test_outputs/passthrough_and_naming.rs
+++ b/crates/cxx-qt-gen/test_outputs/passthrough_and_naming.rs
@@ -263,7 +263,7 @@ pub mod ffi {
         #[cxx_name = "myRenamedFunction"]
         #[namespace = "second_object"]
         #[doc(hidden)]
-        unsafe fn my_function(self: &SecondObject);
+        unsafe fn my_function(self: &SecondObject, param: i32);
     }
     unsafe extern "C++" {
         #[cxx_name = "ready"]
@@ -780,6 +780,9 @@ cxx_qt::static_assertions::assert_eq_size!(
     cxx_qt::signalhandler::CxxQtSignalHandler<SecondObjectCxxQtSignalClosurepropertyNameChanged>,
     [usize; 2]
 );
+unsafe fn my_function(self: &SecondObject, param: i32) {
+    self.rust().my_function(param)
+}
 impl ffi::SecondObject {
     #[doc = "Connect the given function pointer to the signal "]
     #[doc = "ready"]