Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Option型を返す関数での利用

属性でフック対象判定を制御する 方法を紹介していましたが、イマイチピンとこなかったと思います。そもそも滅多に触ることはない設定だろうなと作者も考えています。

それでもなるべくイメージが付きやすい例ということで、 ( Result 型ではなく) Option 型にフックすることのみを目的とした設定を施してみましょう。

次の例では、main関数内に定義した関数にフックをしています。(ネストできることについては mod以下の関数に一括適用を参照してください。) Some であったときに中身を確認するメソッドを挿入したいというシナリオです。

  • tail_expr_idents: 今回、 Some にのみフックしたいので Some を指定
  • ignore_expr_idents: None にフックを仕掛けるのは無駄なので(ほかに条件がそろっている時でも) None にはフックしないようにする
  • result_types: Option 型関数の時は末尾式や return に来る値の型をフック対象だとみなすようになる
use std::fmt::Debug;

use hooq::hooq;

#[hooq]
#[hooq::method(.my_inspect())]
#[hooq::tail_expr_idents("Some")]
#[hooq::ignore_tail_expr_idents("None")]
#[hooq::result_types("Option")]
fn main() {
    fn option_fn_1() -> Option<i32> {
        // hook target
        Some(42)
    }

    fn option_fn_2<T: Debug>(flag: bool, val: T) -> Option<T> {
        // hook target
        let _ = option_fn_1()?;

        // hook target because the return type of the function is Option
        if flag {
            // hook target
            Some(val)
        } else {
            // NOT hook target
            None
        }
    }

    fn result_fn_1() -> Result<i32, ()> {
        // NOT hook target because the return type of the function is Result not Option
        Ok(42)
    }

    fn result_fn_2() -> Result<i32, ()> {
        // HOOK TARGET because of `?`
        // so, #[hooq::skip_all] is needed
        #[hooq::skip_all]
        let _ = result_fn_1()?;

        // NOT hook target because the return type of the function is Result not Option
        Ok(42)
    }

    let _ = option_fn_1();
    let _ = option_fn_2(true, 123);
    let _ = result_fn_1();
    let _ = result_fn_2();
}

trait MyInspect {
    fn my_inspect(self) -> Self;
}

impl<T> MyInspect for Option<T>
where
    T: Debug,
{
    fn my_inspect(self) -> Self {
        match self {
            Some(val) => {
                println!("Inspecting value: {:?}", val);
                Some(val)
            }
            None => None,
        }
    }
}

前レシピのmod以下の関数に一括適用と同じぐらいのパズルになってしまいましたね…

展開結果は次のようになります。

#![feature(prelude_import)]
#[macro_use]
extern crate std;
#[prelude_import]
use std::prelude::rust_2024::*;
use std::fmt::Debug;
use hooq::hooq;
fn main() {
    fn option_fn_1() -> Option<i32> {
        Some(42).my_inspect()
    }
    fn option_fn_2<T: Debug>(flag: bool, val: T) -> Option<T> {
        let _ = option_fn_1().my_inspect()?;
        if flag { Some(val).my_inspect() } else { None }.my_inspect()
    }
    fn result_fn_1() -> Result<i32, ()> {
        Ok(42)
    }
    fn result_fn_2() -> Result<i32, ()> {
        let _ = result_fn_1()?;
        Ok(42)
    }
    let _ = option_fn_1();
    let _ = option_fn_2(true, 123);
    let _ = result_fn_1();
    let _ = result_fn_2();
}
trait MyInspect {
    fn my_inspect(self) -> Self;
}
impl<T> MyInspect for Option<T>
where
    T: Debug,
{
    fn my_inspect(self) -> Self {
        match self {
            Some(val) => {
                {
                    ::std::io::_print(format_args!("Inspecting value: {0:?}\n", val));
                };
                Some(val)
            }
            None => None,
        }
    }
}

ともかく本節で主張したかったのは、基本的にはフック対象の型や識別子は Result, Ok, Err から変更することはないかもしれませんが、それでもhooqは対象となる型・識別子の設定を完全に固定しているわけではなく、一応設定変更可能にしているということです。

将来的に Tryトレイト 周りがstableになった時などに利用の幅が広がればと考えています。