@@ -634,14 +634,10 @@ pub const Lua = struct {
634634 return self .state ().objLen (-1 );
635635 }
636636
637- /// Creates an iterator over table entries .
637+ /// Creates an iterator for this table .
638638 ///
639- /// Advances table iteration by taking the current entry and returning the next one.
640- /// Automatically deallocates the passed entry's resources before returning.
641- ///
642- /// To start iteration, pass `null` as the entry parameter.
643- /// When iteration is complete, returns `null`. Note that the passed entry
644- /// is always deallocated, even when iteration ends.
639+ /// The returned iterator automatically handles all resource management
640+ /// for safe iteration over table entries.
645641 ///
646642 /// Examples:
647643 /// ```zig
@@ -651,59 +647,20 @@ pub const Lua = struct {
651647 /// try table.set("name", "Alice");
652648 /// try table.set(1, "first");
653649 ///
654- /// var current: ?Table.Entry = null;
655- /// while (try table.next(current)) |entry| {
656- /// current = entry;
657- ///
658- /// // Use helper methods to access values
650+ /// var iterator = table.iterator();
651+ /// while (try iterator.next()) |entry| {
659652 /// if (entry.key.asString()) |s| {
660653 /// std.debug.print("String key: {s}\n", .{s});
661- /// } else if (entry.key.asNumber()) |n| {
662- /// std.debug.print("Number key: {d}\n", .{n});
663- /// }
664- ///
665- /// if (entry.value.asString()) |s| {
666- /// std.debug.print("String value: {s}\n", .{s});
667- /// } else if (entry.value.asInt()) |i| {
668- /// std.debug.print("Int value: {d}\n", .{i});
669654 /// }
670655 /// }
671656 /// ```
672657 ///
673- /// Parameters:
674- /// - `current_entry`: The current entry from previous iteration, or `null` to start
675- ///
676- /// Returns: `?Entry` - The next key-value pair, or `null` if iteration is complete
677- /// Errors: `Error.OutOfMemory` if stack allocation fails
678- pub fn next (self : Table , current_entry : ? Entry ) ! ? Entry {
679- defer if (current_entry ) | entry | entry .deinit ();
680-
681- try self .ref .lua .checkStack (3 );
682-
683- // Push table onto stack
684- stack .push (self .ref .lua , self .ref );
685- defer self .state ().pop (1 );
686-
687- // Push key for lua_next (nil if null)
688- if (current_entry ) | entry | {
689- stack .push (self .ref .lua , entry .key );
690- } else {
691- self .state ().pushNil ();
692- }
693-
694- // Call lua_next: pops key, pushes next key-value pair (or nothing if done)
695- if (self .state ().next (-2 )) {
696- // Stack now has: table, key, value
697-
698- // Pop value and key
699- const value = stack .pop (self .ref .lua , Lua .Value ).? ;
700- const key = stack .pop (self .ref .lua , Lua .Value ).? ;
701-
702- return Entry { .key = key , .value = value };
703- } else {
704- // No more entries
705- return null ;
706- }
658+ /// Returns: `Iterator` - A new iterator instance ready for use
659+ pub fn iterator (self : Table ) Iterator {
660+ return Iterator {
661+ .table = self ,
662+ .current_entry = null ,
663+ };
707664 }
708665
709666 /// Set the readonly state of this table.
@@ -788,19 +745,79 @@ pub const Lua = struct {
788745 }
789746
790747 /// Entry representing a key-value pair from table iteration.
791- /// Must be explicitly cleaned up using `deinit()` or passed to the next
792- /// `next()` call for automatic deallocation.
748+ /// Resources are automatically managed when using the `Iterator` type.
793749 pub const Entry = struct {
794750 key : Lua.Value ,
795751 value : Lua.Value ,
796752
797753 /// Releases the resources held by this entry.
798- /// Note: This is automatically called when passing the entry to `next()` .
754+ /// Note: This is automatically called by the Iterator .
799755 pub fn deinit (self : Entry ) void {
800756 self .key .deinit ();
801757 self .value .deinit ();
802758 }
803759 };
760+
761+ /// Iterator for table entries.
762+ ///
763+ /// The iterator handles all entry cleanup automatically.
764+ ///
765+ /// Examples:
766+ /// ```zig
767+ /// const table = lua.createTable(.{});
768+ /// defer table.deinit();
769+ ///
770+ /// try table.set("name", "Alice");
771+ /// try table.set(1, "first");
772+ ///
773+ /// var iterator = table.iterator();
774+ /// while (try iterator.next()) |entry| {
775+ /// if (entry.key.asString()) |s| {
776+ /// std.debug.print("String key: {s}\n", .{s});
777+ /// }
778+ /// }
779+ /// ```
780+ pub const Iterator = struct {
781+ table : Table ,
782+ current_entry : ? Entry ,
783+
784+ /// Advances the iterator and returns the next entry, or null if done.
785+ /// Automatically manages cleanup of previous entries.
786+ ///
787+ /// Returns: `?*const Entry` - Pointer to the next entry, or null if iteration complete
788+ /// Errors: `Error.OutOfMemory` if stack allocation fails
789+ pub fn next (self : * Iterator ) ! ? * const Entry {
790+ try self .table .ref .lua .checkStack (3 );
791+
792+ // Push table onto stack
793+ stack .push (self .table .ref .lua , self .table .ref );
794+ defer self .table .state ().pop (1 );
795+
796+ // Push key for lua_next (nil if null)
797+ if (self .current_entry ) | entry | {
798+ stack .push (self .table .ref .lua , entry .key );
799+ entry .deinit ();
800+ } else {
801+ self .table .state ().pushNil ();
802+ }
803+
804+ // Call lua_next: pops key, pushes next key-value pair (or nothing if done)
805+ if (self .table .state ().next (-2 )) {
806+ // Stack now has: table, key, value
807+
808+ // Pop value and key
809+ const value = stack .pop (self .table .ref .lua , Lua .Value ).? ;
810+ const key = stack .pop (self .table .ref .lua , Lua .Value ).? ;
811+
812+ self .current_entry = Entry { .key = key , .value = value };
813+ return & self .current_entry .? ;
814+ } else {
815+ // No more entries
816+ self .current_entry = null ;
817+ return null ;
818+ }
819+ }
820+ };
804821 };
805822
806823 /// Generic Lua value that can represent any runtime Lua type.
0 commit comments