Rustでlazy_staticを使ってグローバルでイミュータブルな定数を初期化してみる

以前Rustでチケットモデリングの実装をしたとき、グローバルでイミュータブルな定数を実装できず毎回初期化していたのですが、lazy_staticを使えば解決できるとのことだったので試してみました。 steavevaivai.hatenablog.com

詳しくは以下のQiitaの記事が詳しかったので、それを参考にlazy_staticのクレートを追加して修正を行いました。 qiita.com

具体的な修正部分は以下になるのですが、 github.com

lazy_static! {
    pub static ref all_plans: Vec<Plan> = {
        fn make_weekday_notlate_plan(
            name: PlanName,
            customer_spec: CustomerSpec,
            price: i32,
        ) -> Plan {
            Plan {
                name: name,
                price: price,
                spec: PlanSpecification {
                    customer_spec: Box::new(customer_spec),
                    business_day_spec_opt: Option::Some(Spec::And(
                        Box::new(Spec::Normal(Box::new(BusinessDaySpec::Weekday))),
                        Box::new(Spec::Not(Box::new(Spec::Normal(Box::new(LateSpec8))))),
                    )),
                    movie_day_spec_opt: Option::None,
                },
            }
        };
        let mut weekday_notlate_plans = vec![
            make_weekday_notlate_plan(
                PlanName::CinemaCitizenSenior,
                CustomerSpec::CinemaCitizenSenior,
                1000,
            ),
            make_weekday_notlate_plan(
                PlanName::CinemaCitizen,
                CustomerSpec::CinematicCitizen,
                1000,
            ),
            make_weekday_notlate_plan(PlanName::General, CustomerSpec::General, 1800),
            make_weekday_notlate_plan(PlanName::Senior, CustomerSpec::Senior, 1100),
            make_weekday_notlate_plan(
                PlanName::UniversityStudent,
                CustomerSpec::UniversityStudent,
                1500,
            ),
            make_weekday_notlate_plan(
                PlanName::HighSchoolStudent,
                CustomerSpec::HighSchoolStudent,
                1000,
            ),
        ];
        fn make_weekday_late_plan(name: PlanName, customer_spec: CustomerSpec, price: i32) -> Plan {
            Plan {
                name: name,
                price: price,
                spec: PlanSpecification {
                    customer_spec: Box::new(customer_spec),
                    business_day_spec_opt: Option::Some(Spec::And(
                        Box::new(Spec::Normal(Box::new(BusinessDaySpec::Weekday))),
                        Box::new(Spec::Normal(Box::new(LateSpec8))),
                    )),
                    movie_day_spec_opt: Option::None,
                },
            }
        };
        let mut weekday_late_plans = vec![
            make_weekday_late_plan(
                PlanName::CinemaCitizen,
                CustomerSpec::CinematicCitizen,
                1000,
            ),
            make_weekday_late_plan(
                PlanName::CinemaCitizenSenior,
                CustomerSpec::CinematicCitizen,
                1000,
            ),
            make_weekday_late_plan(PlanName::Senior, CustomerSpec::Senior, 1100),
            make_weekday_late_plan(PlanName::General, CustomerSpec::General, 1300),
            make_weekday_late_plan(
                PlanName::UniversityStudent,
                CustomerSpec::UniversityStudent,
                1300,
            ),
            make_weekday_late_plan(
                PlanName::HighSchoolStudent,
                CustomerSpec::HighSchoolStudent,
                1000,
            ),
        ];

        fn make_holiday_notlate_plan(
            name: PlanName,
            customer_spec: CustomerSpec,
            price: i32,
        ) -> Plan {
            Plan {
                name: name,
                price: price,
                spec: PlanSpecification {
                    customer_spec: Box::new(customer_spec),
                    business_day_spec_opt: Option::Some(Spec::And(
                        Box::new(Spec::Normal(Box::new(BusinessDaySpec::Holiday))),
                        Box::new(Spec::Not(Box::new(Spec::Normal(Box::new(LateSpec8))))),
                    )),
                    movie_day_spec_opt: Option::None,
                },
            }
        };
        let mut holiday_notlate_plans = vec![
            make_holiday_notlate_plan(
                PlanName::CinemaCitizen,
                CustomerSpec::CinematicCitizen,
                1300,
            ),
            make_holiday_notlate_plan(
                PlanName::CinemaCitizenSenior,
                CustomerSpec::CinemaCitizenSenior,
                1000,
            ),
            make_holiday_notlate_plan(
                PlanName::CinemaCitizenSenior,
                CustomerSpec::CinemaCitizenSenior,
                1100,
            ),
            make_holiday_notlate_plan(PlanName::General, CustomerSpec::General, 1800),
        ];
        let mut all = Vec::new();
        all.append(&mut weekday_notlate_plans);
        all.append(&mut weekday_late_plans);
        all.append(&mut holiday_notlate_plans);

        fn sort(mut plans: Vec<Plan>) -> Vec<Plan> {
            plans.sort_by(|a, b| a.price.cmp(&b.price));
            plans
        }
        sort(all)
    };
}

これだけだと ~ cannot be shared between threads safely といったエラーメッセージが表示されます。グローバル(スレッド間)でも安全にデータを扱えるためには、対象のデータに対してSyncを実装しておく必要があるので今回は以下のようにunsafeでSyncをImplしておけばエラーが解消されることが確認できました。

unsafe impl Sync for Plan {}