Rust中如何遍历实现Currency trait的所有类型以完成货币代码解析?
Great question! Let's break down how to solve this problem step by step, since Rust's static type system means we can't automatically iterate over all trait implementations out of the box. First, let's fix a small issue in your existing code, then implement the parsing logic.
Step 1: Fix the Currency Trait Definition
Your current TICKER_SYMBOL uses String, which won't work for compile-time constants—to_string() is a runtime method. We need to use a &'static str instead, since string literals are statically available at compile time:
pub trait Currency { const TICKER_SYMBOL: &'static str; }
Update your USD implementation to match (add Clone/Copy for easier instance creation):
#[derive(Debug, Clone, Copy)] struct USD; impl Currency for USD { const TICKER_SYMBOL: &'static str = "USD"; }
Step 2: Choose a Strategy for Enumerating Currencies
Rust doesn't have reflection, so we can't automatically collect all types that implement Currency. We have two common, practical approaches here:
Option 1: Trait Objects with a Static Factory List
This approach is flexible if you plan to add more currencies later without changing core parsing logic. We'll return a Box<dyn Currency> (a trait object) and maintain a list of functions that instantiate each currency type:
use std::str::FromStr; impl FromStr for Box<dyn Currency> { type Err = &'static str; fn from_str(s: &str) -> Result<Self, Self::Err> { // Manually list all Currency implementations here let currency_factories: &[fn() -> Box<dyn Currency>] = &[ || Box::new(USD), // Add new currencies here as needed, e.g.: // || Box::new(EUR), // || Box::new(GBP), ]; // Iterate through factories to find a matching ticker for factory in currency_factories { let currency = factory(); if currency.TICKER_SYMBOL == s { return Ok(currency); } } Err("Unsupported currency ticker symbol") } }
Option 2: Enum Wrapper (No Dynamic Dispatch)
If you prefer to avoid trait objects and dynamic dispatch, wrap all your currency types in an enum. This is more efficient and lets you work with concrete, type-safe values:
First, define the enum and add helper logic to access tickers:
#[derive(Debug, Clone, Copy, PartialEq, Eq)] enum CurrencyEnum { USD, EUR, // Add new variants here as needed } // Helper method to get the ticker for each enum variant impl CurrencyEnum { pub fn ticker_symbol(&self) -> &'static str { match self { Self::USD => USD::TICKER_SYMBOL, Self::EUR => EUR::TICKER_SYMBOL, } } } // Implement FromStr for the enum impl FromStr for CurrencyEnum { type Err = &'static str; fn from_str(s: &str) -> Result<Self, Self::Err> { match s { USD::TICKER_SYMBOL => Ok(Self::USD), EUR::TICKER_SYMBOL => Ok(Self::EUR), _ => Err("Unsupported currency ticker symbol"), } } }
Step 3: Test the Implementation
Let's verify both approaches work as expected:
fn main() { // Test trait object approach let usd_trait: Box<dyn Currency> = "USD".parse().unwrap(); println!("Trait object: {:?}", usd_trait); // Test enum approach let eur_enum: CurrencyEnum = "EUR".parse().unwrap(); println!("Enum variant: {:?}", eur_enum); println!("EUR ticker: {}", eur_enum.ticker_symbol()); }
Key Notes
- No Automatic Trait Collection: Rust's static type system prevents us from automatically finding all
Currencyimplementations—you'll need to update the factory list or enum variants when adding new currencies. - Compile-Time Safety: Using
&'static strensures ticker symbols are available at compile time, avoiding runtime allocation and errors. - Tradeoffs: Trait objects offer flexibility for dynamic extension, while enums provide better performance and strict type safety. Choose based on your project's needs.
内容的提问来源于stack exchange,提问作者Edward

