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

Using in Functions Returning Option

We previously introduced controlling hook target decisions via attributes, which may feel abstract. Here is a more concrete scenario: target only Option values.

In the example below, we hook functions defined inside main (nesting is supported; see Batch apply). We want to inspect when a value is Some.

  • tail_expr_idents: set to Some to hook only Some.
  • ignore_tail_expr_idents: set to None to avoid wasteful hooks.
  • result_types: set to Option so tail/return values are considered.
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,
        }
    }
}

Expansion:

#![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,
        }
    }
}

The point: while Result/Ok/Err are common defaults, hooq does not hard‑code these; you can target other types/idents.

Potentially useful once Try trait stabilizes.