扩展星盘

用扩展 trait 给 Astrolabe、PalaceRef 补自定义分析方法。

斗数的分析规则千人千面,库不可能穷举。x-iztro 的做法是让你把自己的规则 以方法的形式挂到星盘上——调用语法与内置方法一致,且在编译期完成,类型受检、零运行期开销。

配方

定义一个 trait,声明你要补的方法
Astrolabe(或 PalaceRefStarRef)实现它
使用处 use 这个 trait,方法即可用
use x_iztro::i18n::translate_star;
use x_iztro::*;

/// 给星盘补两个自定义分析方法。
trait MyAnalysis {
    /// 命宫主星名(空宫借对宫),多颗以逗号分隔
    fn major_star(&self) -> String;
    /// 五行局的局数
    fn five_elements_value(&self) -> usize;
}

impl MyAnalysis for Astrolabe {
    fn major_star(&self) -> String {
        let soul = self.palace(Palace::Soul).expect("命宫必然存在");
        let source = if soul.is_empty() { soul.opposite_palace() } else { soul };

        source
            .major_stars
            .iter()
            .filter(|s| s.star_type == StarType::Major)
            .map(|s| translate_star(s.key, self.language))
            .collect::<Vec<_>>()
            .join(",")
    }

    fn five_elements_value(&self) -> usize {
        self.five_elements_class.value()
    }
}

用法

let chart = by_solar("2000-8-16", 2, Gender::Female, true, Language::ZhCN, Config::default())?;

println!("{}", chart.major_star());
println!("{}", chart.five_elements_value());

// 扩展方法随排盘语言输出
let en = by_solar("2000-8-16", 2, Gender::Female, true, Language::EnUS, Config::default())?;
println!("{}", en.major_star());

输出

紫微
3
emperor

扩展别的类型

同一套写法适用于宫位与星耀。为视图类型实现时注意生命周期参数:

trait PalaceAnalysis {
    /// 本宫是否「煞忌交冲」:坐煞星且带化忌
    fn is_afflicted(&self) -> bool;
}

impl PalaceAnalysis for PalaceRef<'_> {
    fn is_afflicted(&self) -> bool {
        use x_iztro::StarKey::*;
        self.has_one_of(&[QingyangMin, TuoluoMin, HuoxingMin, LingxingMin, DikongMin, DijieMin])
            && self.has_mutagen(Mutagen::Ji)
    }
}
for palace in &chart.palaces {
    let p = chart.palace(palace.index).unwrap();
    if p.is_afflicted() {
        println!("{} 煞忌交冲", translate_palace(p.name, Language::ZhCN));
    }
}

输出

疾厄 煞忌交冲

组织建议


与运行期注入的区别

有些库让你在运行期把函数挂到对象上。Rust 的扩展 trait 在编译期完成同一件事, 差别在于:

扩展 trait运行期注入
方法是否存在编译期确定运行期才知道
类型检查
调用开销与内置方法相同多一次动态查找
出错时机编译失败运行时报错
作用范围use 了才可见全局或按实例

代价是扩展方法必须在编译期就写好,不能由配置文件或用户输入动态决定。 需要那种灵活度时,用一张 HashMap<String, fn(&Astrolabe) -> bool> 自行分派。

本页目录