@@ -19,7 +19,9 @@ use crate::util::{
1919/// See [`Lua::scope`] for more details.
2020pub struct Scope < ' scope , ' env : ' scope > {
2121 lua : LuaGuard ,
22+ // Internal destructors run first, then user destructors (based on the declaration order)
2223 destructors : Destructors < ' env > ,
24+ user_destructors : UserDestructors < ' env > ,
2325 _scope_invariant : PhantomData < & ' scope mut & ' scope ( ) > ,
2426 _env_invariant : PhantomData < & ' env mut & ' env ( ) > ,
2527}
@@ -29,11 +31,14 @@ type DestructorCallback<'a> = Box<dyn FnOnce(&RawLua, ValueRef) -> Vec<Box<dyn F
2931// Implement Drop on Destructors instead of Scope to avoid compilation error
3032struct Destructors < ' a > ( RefCell < Vec < ( ValueRef , DestructorCallback < ' a > ) > > ) ;
3133
34+ struct UserDestructors < ' a > ( RefCell < Vec < Box < dyn FnOnce ( ) + ' a > > > ) ;
35+
3236impl < ' scope , ' env : ' scope > Scope < ' scope , ' env > {
3337 pub ( crate ) fn new ( lua : LuaGuard ) -> Self {
3438 Scope {
3539 lua,
3640 destructors : Destructors ( RefCell :: new ( Vec :: new ( ) ) ) ,
41+ user_destructors : UserDestructors ( RefCell :: new ( Vec :: new ( ) ) ) ,
3742 _scope_invariant : PhantomData ,
3843 _env_invariant : PhantomData ,
3944 }
@@ -215,6 +220,31 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
215220 }
216221 }
217222
223+ /// Adds a destructor function to be run when the scope ends.
224+ ///
225+ /// This functionality is useful for cleaning up any resources after the scope ends.
226+ ///
227+ /// # Example
228+ ///
229+ /// ```rust
230+ /// # use mlua::{Error, Lua, Result};
231+ /// # fn main() -> Result<()> {
232+ /// let lua = Lua::new();
233+ /// let ud = lua.create_any_userdata(String::from("hello"))?;
234+ /// lua.scope(|scope| {
235+ /// scope.add_destructor(|| {
236+ /// _ = ud.take::<String>();
237+ /// });
238+ /// // Run the code that uses `ud` here
239+ /// Ok(())
240+ /// })?;
241+ /// assert!(matches!(ud.borrow::<String>(), Err(Error::UserDataDestructed)));
242+ /// # Ok(())
243+ /// # }
244+ pub fn add_destructor ( & ' scope self , destructor : impl FnOnce ( ) + ' env ) {
245+ self . user_destructors . 0 . borrow_mut ( ) . push ( Box :: new ( destructor) ) ;
246+ }
247+
218248 unsafe fn create_callback ( & ' scope self , f : ScopedCallback < ' scope > ) -> Result < Function > {
219249 let f = mem:: transmute :: < ScopedCallback , Callback > ( f) ;
220250 let f = self . lua . create_callback ( f) ?;
@@ -271,3 +301,12 @@ impl Drop for Destructors<'_> {
271301 }
272302 }
273303}
304+
305+ impl Drop for UserDestructors < ' _ > {
306+ fn drop ( & mut self ) {
307+ let destructors = mem:: take ( & mut * self . 0 . borrow_mut ( ) ) ;
308+ for destructor in destructors {
309+ destructor ( ) ;
310+ }
311+ }
312+ }
0 commit comments