From bedf859a7240b3c9899eaf100cfe3d7a5a5da185 Mon Sep 17 00:00:00 2001 From: bhollier Date: Tue, 19 Dec 2023 17:45:30 +0000 Subject: [PATCH] refactor Stack to hold values instead of refs --- src/card.rs | 34 ++++----------- src/game_state.rs | 6 +-- src/std/card.rs | 2 +- src/std/game_state.rs | 71 +++++++++++++++++++------------- src/variant/klondike.rs | 29 +++++++------ tests/card.rs | 20 ++------- tests/variant/klondike.rs | 87 ++++++++++++--------------------------- 7 files changed, 102 insertions(+), 147 deletions(-) diff --git a/src/card.rs b/src/card.rs index 917edda..4dc18a4 100644 --- a/src/card.rs +++ b/src/card.rs @@ -11,25 +11,19 @@ pub trait Card: Copy + Clone + Eq + Ord + Hash { /// A Deck of [Card]s pub type Deck = [C; N]; -/// A "Stack" of [Card] references, usually the [Card]s referencing the elements of a [Deck] +/// A "Stack" of [Card]s // todo change this to be backed by an array and benchmark -pub type Stack<'d, C> = Vec<&'d C>; +pub type Stack = Vec; /// Trait extension for adding `from_*` functions to [Stack] -pub trait StackFrom { - /// Convenience function to create a [Stack] from a slice of `C` (usually a [Deck] or slice of [Card]). - /// Intended to be used to construct from a deck, as a stack usually contains reference to cards within a deck +pub trait StackFrom, const N: usize> { + /// Convenience function to create a [Stack] from a slice of `C` (usually a [Deck] or slice of [Card]) fn from_slice(cs: &[C]) -> Stack { - cs.iter().collect() - } - - /// Convenience function to create a [Stack] from a [Vec] - fn from_vec(cs: &Vec) -> Stack { - Self::from_slice(cs.as_slice()) + cs.iter().cloned().collect() } } -impl<'d, C> StackFrom for Stack<'d, C> {} +impl, const N: usize> StackFrom for Stack {} /// Shuffles the given deck mutably, using [rand::thread_rng()] pub fn shuffle, const N: usize>(d: &mut Deck) { @@ -49,15 +43,9 @@ pub fn take_n_slice(slice: &[T], n: usize) -> (&[T], &[T]) { (&slice[0..slice.len() - n], &slice[slice.len() - n..]) } -/// Creates two [Vec] from the given [Vec], as a tuple of the `(remaining, taken)` -pub fn take_n_vec<'a, T>(cs: &Vec<&'a T>, n: usize) -> (Vec<&'a T>, Vec<&'a T>) { - let (rest, cs) = take_n_slice(cs.as_slice(), n); - (rest.iter().cloned().collect(), cs.iter().cloned().collect()) -} - /// Creates `n` elements from the given [Vec] and returns it, /// modifying the given [Vec] in the process -pub fn take_n_vec_mut<'a, T>(cs: &mut Vec<&'a T>, n: usize) -> Vec<&'a T> { +pub fn take_n_vec_mut(cs: &mut Vec, n: usize) -> Vec { cs.split_off(cs.len() - n) } @@ -67,13 +55,7 @@ pub fn take_one_slice(cs: &[T]) -> (&[T], &T) { (rest, &cs[0]) } -/// Returns the "top" card and a [Vec] of the remaining elements -pub fn take_one_vec<'a, T>(cs: &Vec<&'a T>) -> (Vec<&'a T>, &'a T) { - let (rest, cs) = take_n_slice(cs.as_slice(), 1); - (rest.iter().cloned().collect(), cs[0]) -} - /// Returns the "top" card and removes the element from the given [Vec] -pub fn take_one_vec_mut<'a, T>(cs: &mut Vec<&'a T>) -> &'a T { +pub fn take_one_vec_mut(cs: &mut Vec) -> T { cs.pop().unwrap() } diff --git a/src/game_state.rs b/src/game_state.rs index f70c01e..e5cee67 100644 --- a/src/game_state.rs +++ b/src/game_state.rs @@ -6,12 +6,12 @@ use thiserror; pub trait PileRef: Eq {} /// Trait for the state of a Solitaire game -pub trait GameState<'d, C: Card, const N: usize, P: PileRef>: Sized + Clone + Eq { +pub trait GameState, const N: usize, P: PileRef>: Sized + Clone + Eq { /// Retrieve a reference to the [Stack] at the given [PileRef] - fn get_stack(&self, p: P) -> Option<&Stack<'d, C>>; + fn get_stack(&self, p: P) -> Option<&Stack>; /// Retrieve a mutable reference to the [Stack] at the given [PileRef] - fn get_stack_mut(&mut self, p: P) -> Option<&mut Stack<'d, C>>; + fn get_stack_mut(&mut self, p: P) -> Option<&mut Stack>; } /// Enum of all the possible errors that may occur while operating on a [GameState] diff --git a/src/std/card.rs b/src/std/card.rs index 1e0c5ab..5a3ad1c 100644 --- a/src/std/card.rs +++ b/src/std/card.rs @@ -156,4 +156,4 @@ impl solitaire::Card<{ Card::N }> for Card { pub type Deck = solitaire::Deck; /// Convenience type alias for a [Stack](solitaire::Stack) of [Card] -pub type Stack<'d> = solitaire::Stack<'d, Card>; +pub type Stack = solitaire::Stack; diff --git a/src/std/game_state.rs b/src/std/game_state.rs index 4c3ceb6..f52656c 100644 --- a/src/std/game_state.rs +++ b/src/std/game_state.rs @@ -1,5 +1,5 @@ use crate as solitaire; -use crate::{Card, GameState, Stack, StackFrom}; +use crate::{shuffle, Card, GameState, Stack, StackFrom, Deck}; /// "Standard" solitaire piles #[derive(Eq, PartialEq, Copy, Clone)] @@ -24,20 +24,20 @@ impl solitaire::PileRef for PileRef {} /// Struct for the initial [GameState] with just the [Stock](PileRef::Stock) pile #[derive(Clone, Debug, Eq, PartialEq)] -pub struct InitialGameState<'d, C: Card, const N: usize> { +pub struct InitialGameState, const N: usize> { /// The stock, see [Stock](PileRef::Stock) - pub stock: Stack<'d, C>, + pub stock: Stack, } -impl<'d, C: Card, const N: usize> GameState<'d, C, N, PileRef> for InitialGameState<'d, C, N> { - fn get_stack(&self, p: PileRef) -> Option<&Stack<'d, C>> { +impl, const N: usize> GameState for InitialGameState { + fn get_stack(&self, p: PileRef) -> Option<&Stack> { match p { PileRef::Stock => Some(&self.stock), _ => None, } } - fn get_stack_mut(&mut self, p: PileRef) -> Option<&mut Stack<'d, C>> { + fn get_stack_mut(&mut self, p: PileRef) -> Option<&mut Stack> { match p { PileRef::Stock => Some(&mut self.stock), _ => None, @@ -45,34 +45,42 @@ impl<'d, C: Card, const N: usize> GameState<'d, C, N, PileRef> for InitialGam } } -impl<'d, C: Card, const N: usize> InitialGameState<'d, C, N> { - pub fn new(deck: &'d [C]) -> InitialGameState<'d, C, N> { +impl, const N: usize> InitialGameState { + pub fn new() -> InitialGameState { + let mut d = C::new_deck(); + shuffle(&mut d); + InitialGameState::from(d) + } +} + +impl, const N: usize> From> for InitialGameState { + fn from(d: Deck) -> Self { InitialGameState { - stock: Stack::from_slice(deck), + stock: Stack::from_slice(&d), } } } /// Struct for a mid-game "playing" [GameState] with four [piles](PileRef) of generic [Card]s #[derive(Clone, Debug, Eq, PartialEq)] -pub struct PlayingGameState<'d, C: Card, const NC: usize, const NT: usize, const NF: usize> { +pub struct PlayingGameState, const NC: usize, const NT: usize, const NF: usize> { /// The tableau, see [Tableau](PileRef::Tableau) - pub tableau: [Stack<'d, C>; NT], + pub tableau: [Stack; NT], /// The foundations, see [Foundation](PileRef::Foundation) - pub foundations: [Stack<'d, C>; NF], + pub foundations: [Stack; NF], /// The stock, see [Stock](PileRef::Stock) - pub stock: Stack<'d, C>, + pub stock: Stack, /// The talon, see [Talon](PileRef::Talon) - pub talon: Stack<'d, C>, + pub talon: Stack, } -impl<'d, C: Card, const NC: usize, const NT: usize, const NF: usize> - GameState<'d, C, NC, PileRef> for PlayingGameState<'d, C, NC, NT, NF> +impl, const NC: usize, const NT: usize, const NF: usize> GameState + for PlayingGameState { - fn get_stack(&self, p: PileRef) -> Option<&Stack<'d, C>> { + fn get_stack(&self, p: PileRef) -> Option<&Stack> { match p { PileRef::Tableau(n) => self.tableau.get(n), PileRef::Foundation(n) => self.foundations.get(n), @@ -81,7 +89,7 @@ impl<'d, C: Card, const NC: usize, const NT: usize, const NF: usize> } } - fn get_stack_mut(&mut self, p: PileRef) -> Option<&mut Stack<'d, C>> { + fn get_stack_mut(&mut self, p: PileRef) -> Option<&mut Stack> { match p { PileRef::Tableau(n) => self.tableau.get_mut(n), PileRef::Foundation(n) => self.foundations.get_mut(n), @@ -93,22 +101,22 @@ impl<'d, C: Card, const NC: usize, const NT: usize, const NF: usize> /// Struct for a win [GameState] with just the [Foundation](PileRef::Foundation) piles #[derive(Clone, Debug, Eq, PartialEq)] -pub struct WinGameState<'d, C: Card, const NC: usize, const NF: usize> { +pub struct WinGameState, const NC: usize, const NF: usize> { /// The foundations, see [Foundation](PileRef::Foundation) - pub foundations: [Stack<'d, C>; NF], + pub foundations: [Stack; NF], } -impl<'d, C: Card, const NC: usize, const NF: usize> GameState<'d, C, NC, PileRef> - for WinGameState<'d, C, NC, NF> +impl<'d, C: Card, const NC: usize, const NF: usize> GameState + for WinGameState { - fn get_stack(&self, p: PileRef) -> Option<&Stack<'d, C>> { + fn get_stack(&self, p: PileRef) -> Option<&Stack> { match p { PileRef::Foundation(n) => self.foundations.get(n), _ => None, } } - fn get_stack_mut(&mut self, p: PileRef) -> Option<&mut Stack<'d, C>> { + fn get_stack_mut(&mut self, p: PileRef) -> Option<&mut Stack> { match p { PileRef::Foundation(n) => self.foundations.get_mut(n), _ => None, @@ -116,10 +124,17 @@ impl<'d, C: Card, const NC: usize, const NF: usize> GameState<'d, C, NC, Pil } } +/// Enum for all possible [GameState]s +pub enum GameStateOption, const NC: usize, const NT: usize, const NF: usize> { + Initial(InitialGameState), + Playing(PlayingGameState), + Win(WinGameState), +} + /// Enum for the resulting [GameState] after making a move, -/// either [Playing] (game not finished) or [Win] +/// either [Playing](PlayingGameState) (game not finished) or [Win](WinGameState) #[derive(Clone, Debug, Eq, PartialEq)] -pub enum MoveResult<'d, C: Card, const NC: usize, const NT: usize, const NF: usize> { - Playing(PlayingGameState<'d, C, NC, NT, NF>), - Win(WinGameState<'d, C, NC, NF>), +pub enum MoveResult, const NC: usize, const NT: usize, const NF: usize> { + Playing(PlayingGameState), + Win(WinGameState), } diff --git a/src/variant/klondike.rs b/src/variant/klondike.rs index 834f271..7f37483 100644 --- a/src/variant/klondike.rs +++ b/src/variant/klondike.rs @@ -9,19 +9,22 @@ pub const NUM_TABLEAU: usize = 7; pub const NUM_FOUNDATIONS: usize = std::FrenchSuit::N; /// The initial [GameState] for Klondike Solitaire with [std::Card] -pub type InitialGameState<'d> = std::InitialGameState<'d, std::Card, { std::Card::N }>; +pub type InitialGameState = std::InitialGameState; /// The mid-game "playing" [GameState] for Klondike Solitaire with [std::Card] -pub type PlayingGameState<'d> = - std::PlayingGameState<'d, std::Card, { std::Card::N }, NUM_TABLEAU, NUM_FOUNDATIONS>; +pub type PlayingGameState = + std::PlayingGameState; /// The win [GameState] for Klondike Solitaire with [std::Card] -pub type WinGameState<'d> = std::WinGameState<'d, std::Card, { std::Card::N }, NUM_FOUNDATIONS>; +pub type WinGameState = std::WinGameState; + +/// Enum for all possible [GameState]s, for Klondike Solitaire with [std::Card] +pub type GameStateOption = + std::GameStateOption; /// Enum for the resulting [GameState] after making a move, /// for Klondike Solitaire with [std::Card] -pub type MoveResult<'d> = - std::MoveResult<'d, std::Card, { std::Card::N }, NUM_TABLEAU, NUM_FOUNDATIONS>; +pub type MoveResult = std::MoveResult; /// The Game rules for Klondike Solitaire pub struct GameRules; @@ -37,7 +40,7 @@ impl GameRules { talon: Stack::new(), }; - let mut card: &std::Card; + let mut card: std::Card; for i in 0..NUM_TABLEAU { for j in i..NUM_TABLEAU { card = take_one_vec_mut(&mut new_state.stock); @@ -50,8 +53,8 @@ impl GameRules { /// Convenience function to create a new [InitialGameState] /// and then deal the cards with [deal](Self::deal) - pub fn new_and_deal(d: &[std::Card]) -> PlayingGameState { - Self::deal(InitialGameState::new(d)) + pub fn new_and_deal() -> PlayingGameState { + Self::deal(InitialGameState::new()) } /// Draws `n` cards from the [Stock](PileRef::Stock) onto the [Talon](PileRef::Talon). @@ -75,7 +78,7 @@ impl GameRules { // Take the cards from the stock let take = take_n_vec_mut(&mut new_state.stock, n); // Transfer to the talon - take.iter().for_each(|c| new_state.talon.push(*c)); + take.iter().cloned().for_each(|c| new_state.talon.push(c)); } } Ok(new_state) @@ -87,10 +90,10 @@ impl GameRules { /// - [Tableau](PileRef::Tableau): cards must be of alternating [Color](std::Color) and in King to Ace order /// - [Stock](PileRef::Stock): always true /// - [Talon](PileRef::Talon): always true - pub fn valid_seq(p: PileRef, cs: &[&std::Card]) -> bool { + pub fn valid_seq(p: PileRef, cs: &[std::Card]) -> bool { match p { PileRef::Tableau(_) => { - let mut prev_card = cs[0]; + let mut prev_card = &cs[0]; for card in &cs[1..cs.len()] { if card.suit.color() == prev_card.suit.color() { return false; @@ -103,7 +106,7 @@ impl GameRules { return true; } PileRef::Foundation(_) => { - let mut prev_card = cs[0]; + let mut prev_card = &cs[0]; for card in &cs[1..cs.len()] { if card.suit != prev_card.suit { return false; diff --git a/tests/card.rs b/tests/card.rs index b762cca..bfe4bb7 100644 --- a/tests/card.rs +++ b/tests/card.rs @@ -10,17 +10,11 @@ fn test_take_n() { assert_eq!(cs, &d[42..52]); } - { - let (rest, cs) = take_n_vec(&Stack::from_slice(&d), 10); - assert_eq!(rest, d[..42].iter().collect::()); - assert_eq!(cs, d[42..52].iter().collect::()); - } - { let mut d_vec = Stack::from_slice(&d); let cs = take_n_vec_mut(&mut d_vec, 10); - assert_eq!(d_vec, d[..42].iter().collect::()); - assert_eq!(cs, d[42..52].iter().collect::()); + assert_eq!(d_vec, d[..42]); + assert_eq!(cs, d[42..52]); } } @@ -33,16 +27,10 @@ fn test_take_one() { assert_eq!(c, &d[51]); } - { - let (rest, c) = take_one_vec(&Stack::from_slice(&d)); - assert_eq!(rest, d[..51].iter().collect::()); - assert_eq!(c, &d[51]); - } - { let mut d_vec: std::Stack = Stack::from_slice(&d); let c = take_one_vec_mut(&mut d_vec); - assert_eq!(d_vec, d[..51].iter().collect::()); - assert_eq!(c, &d[51]); + assert_eq!(d_vec, d[..51]); + assert_eq!(c, d[51]); } } diff --git a/tests/variant/klondike.rs b/tests/variant/klondike.rs index 03eee70..5a3cfa1 100644 --- a/tests/variant/klondike.rs +++ b/tests/variant/klondike.rs @@ -6,7 +6,7 @@ use test_util::parse; #[test] fn test_game_rules_deal() { let deck: std::Deck = std::Card::new_deck(); - let game = GameRules::new_and_deal(&deck); + let game = GameRules::deal(InitialGameState::from(deck)); // Verify the cards were added to the tableau in the correct order let mut total_taken = 0; @@ -14,14 +14,14 @@ fn test_game_rules_deal() { assert_eq!(stack.len(), i + 1); let mut index = i; for (j, card) in stack.iter().enumerate() { - assert_eq!(*card, &deck[deck.len() - index - 1]); + assert_eq!(card, &deck[deck.len() - index - 1]); index += NUM_TABLEAU - j - 1; total_taken += 1; } } // Verify the stock has the correct cards - let expected_stock: std::Stack = deck[0..deck.len() - total_taken].iter().collect(); + let expected_stock: std::Stack = deck[0..deck.len() - total_taken].iter().cloned().collect(); assert_eq!(game.stock, expected_stock); } @@ -41,23 +41,17 @@ fn test_game_rules_draw_stock() -> Result<()> { game = GameRules::draw_stock(game, 1)?; - assert_eq!(game.stock, vec![&parse::card("KC")]); - assert_eq!(game.talon, vec![&parse::card("AH")]); + assert_eq!(game.stock, vec![parse::card("KC")]); + assert_eq!(game.talon, vec![parse::card("AH")]); game = GameRules::draw_stock(game, 1)?; assert!(game.stock.is_empty()); - assert_eq!( - game.talon, - Stack::from_vec(&parse::cards(&vec!["AH", "KC"])) - ); + assert_eq!(game.talon, parse::cards(&vec!["AH", "KC"])); game = GameRules::draw_stock(game, 1)?; - assert_eq!( - game.stock, - Stack::from_vec(&parse::cards(&vec!["KC", "AH"])) - ); + assert_eq!(game.stock, parse::cards(&vec!["KC", "AH"])); assert!(game.talon.is_empty()); Ok(()) @@ -69,22 +63,16 @@ fn test_game_rules_valid_seq_tableau() { let p = std::PileRef::Tableau(0); let valid = parse::cards(&vec!["KC", "QH", "JS", "XD"]); - assert!(GameRules::valid_seq(p, Stack::from_vec(&valid).as_slice())); + assert!(GameRules::valid_seq(p, valid.as_slice())); - let invalid_wrong_dir: Vec<&std::Card> = valid.iter().rev().collect(); + let invalid_wrong_dir: Vec = valid.iter().rev().cloned().collect(); assert!(!GameRules::valid_seq(p, invalid_wrong_dir.as_slice())); let invalid_same_color = parse::cards(&vec!["8H", "7D", "6D"]); - assert!(!GameRules::valid_seq( - p, - Stack::from_vec(&invalid_same_color).as_slice() - )); + assert!(!GameRules::valid_seq(p, invalid_same_color.as_slice())); let invalid_overflow = parse::cards(&vec!["2C", "AH", "KS"]); - assert!(!GameRules::valid_seq( - p, - Stack::from_vec(&invalid_overflow).as_slice() - )); + assert!(!GameRules::valid_seq(p, invalid_overflow.as_slice())); } /// Test the sequence validation on its own for a foundation pile @@ -93,22 +81,16 @@ fn test_game_rules_valid_seq_foundation() { let p = std::PileRef::Foundation(0); let valid = parse::cards(&vec!["XC", "JC", "QC", "KC"]); - assert!(GameRules::valid_seq(p, Stack::from_vec(&valid).as_slice())); + assert!(GameRules::valid_seq(p, valid.as_slice())); - let invalid_wrong_dir: Vec<&std::Card> = valid.iter().rev().collect(); + let invalid_wrong_dir: Vec = valid.iter().rev().cloned().collect(); assert!(!GameRules::valid_seq(p, invalid_wrong_dir.as_slice())); let invalid_different_suit = parse::cards(&vec!["6D", "7D", "8H"]); - assert!(!GameRules::valid_seq( - p, - Stack::from_vec(&invalid_different_suit).as_slice() - )); + assert!(!GameRules::valid_seq(p, invalid_different_suit.as_slice())); let invalid_overflow = parse::cards(&vec!["QC", "KC", "AC"]); - assert!(!GameRules::valid_seq( - p, - Stack::from_vec(&invalid_overflow).as_slice() - )); + assert!(!GameRules::valid_seq(p, invalid_overflow.as_slice())); } /// Test basic input validation when moving cards, @@ -116,8 +98,7 @@ fn test_game_rules_valid_seq_foundation() { /// or trying to move cards to the stock, etc. #[test] fn test_game_rules_move_cards_invalid_input() { - let deck = std::Card::new_deck(); - let game = GameRules::new_and_deal(&deck); + let game = GameRules::new_and_deal(); let pile = std::PileRef::Tableau(0); @@ -206,8 +187,8 @@ fn test_game_rules_move_cards_invalid_move() -> Result<()> { let mut game = PlayingGameState { tableau: [ - Stack::from_vec(&tableau0), - Stack::from_vec(&tableau1), + tableau0, + tableau1, Stack::new(), Stack::new(), Stack::new(), @@ -307,8 +288,8 @@ fn test_game_rules_move_cards() -> Result<()> { let mut game = PlayingGameState { tableau: [ - Stack::from_vec(&tableau0), - Stack::from_vec(&tableau1), + tableau0, + tableau1, Stack::new(), Stack::new(), Stack::new(), @@ -332,10 +313,7 @@ fn test_game_rules_move_cards() -> Result<()> { // Talon is now empty assert!(game.talon.is_empty()); // Tableau is 2 of Spades and Ace of Hearts - assert_eq!( - game.tableau[0], - Stack::from_vec(&parse::cards(&vec!["2S", "AH"])) - ); + assert_eq!(game.tableau[0], parse::cards(&vec!["2S", "AH"])); // Move the stack to the second tableau with a 3 of Diamonds game = match GameRules::move_cards(game, std::PileRef::Tableau(0), 2, std::PileRef::Tableau(1))? @@ -347,10 +325,7 @@ fn test_game_rules_move_cards() -> Result<()> { // First tableau is now empty assert!(game.tableau[0].is_empty()); // Second tableau is the 3 of Diamonds, 2 of Spades and Ace of Hearts - assert_eq!( - game.tableau[1], - Stack::from_vec(&parse::cards(&vec!["3D", "2S", "AH"])) - ); + assert_eq!(game.tableau[1], parse::cards(&vec!["3D", "2S", "AH"])); // Move the Ace of Hearts to the foundation game = match GameRules::move_cards( @@ -364,12 +339,9 @@ fn test_game_rules_move_cards() -> Result<()> { }; // Tableau is the 3 of Diamonds and 2 of Spades - assert_eq!( - game.tableau[1], - Stack::from_vec(&parse::cards(&vec!["3D", "2S"])) - ); + assert_eq!(game.tableau[1], parse::cards(&vec!["3D", "2S"])); // Foundation is the Ace of Hearts - assert_eq!(game.foundations[0], vec![&parse::card("AH")]); + assert_eq!(game.foundations[0], vec![parse::card("AH")]); // Draw so the King of Clubs is available game = GameRules::draw_stock(game, 1)?; @@ -383,7 +355,7 @@ fn test_game_rules_move_cards() -> Result<()> { // Talon is now empty assert!(game.talon.is_empty()); // Third tableau is King of Clubs - assert_eq!(game.tableau[2], vec![&parse::card("KC")]); + assert_eq!(game.tableau[2], vec![parse::card("KC")]); Ok(()) } @@ -407,7 +379,7 @@ fn test_game_rules_move_cards_win() -> Result<()> { let game = PlayingGameState { tableau: [ - Stack::from_vec(&tableau0), + tableau0, Stack::new(), Stack::new(), Stack::new(), @@ -415,12 +387,7 @@ fn test_game_rules_move_cards_win() -> Result<()> { Stack::new(), Stack::new(), ], - foundations: [ - Stack::from_vec(&foundation0), - Stack::from_vec(&foundation1), - Stack::from_vec(&foundation2), - Stack::from_vec(&foundation3), - ], + foundations: [foundation0, foundation1, foundation2, foundation3], stock: Stack::new(), talon: Stack::new(), };