Option Fn and type inference for None case

Given this:

pub fn mktbldoc<H, B, F>(hbldr: Option<H>, bbldr: B, fbldr: Option<F>) -> Doc
where
  H: Fn(&mut sidoc::Builder),
  B: Fn(&mut sidoc::Builder),
  F: Fn(&mut sidoc::Builder)
{
   // ...
}

.. let's say I want to call mktbldoc(), but I don't want to construct a header or footer:

mktbldoc(None, |foo| {}, None);

The compiler will fail because:

cannot infer type for type parameter `H` declared on the function `mktbldoc`

Any nice ways to solve this on a type level (which isn't overly verbose)? (The solutions I come up cause more runtime penalties).

Heh, basically the same as @2e71828's suggestion in a different thread just now, you could pass in a no-op function.

fn nop(&mut sidoc::Builder) {}
pub fn mktbldoc<H, B, F>(hbldr: H, bbldr: B, fbldr: F) -> Doc
where
  H: Fn(&mut sidoc::Builder),
  B: Fn(&mut sidoc::Builder),
  F: Fn(&mut sidoc::Builder)
{
   todo!()
}
// ...
mktbldoc(nop, |foo| {}, nop);

Although I think in this case, you might not need the nop function.

mktbldoc(|_|{}, |foo| {}, |_|{});
1 Like

The issue I have is this:

  if let Some(hbldr) = hbldr {
    bldr.scope("<thead>", Some("</thead>"));
    hbldr(&mut bldr);
    bldr.exit();
  }

So basically the Some()/None is used to control whether something is added or not in addition to what the callback would add.

Experimenting with this instead:

type DocBldFn = fn(&mut sidoc::Builder);
pub fn mktbldoc2(
  hbldr: Option<DocBldFn>,
  bbldr: DocBldFn,
  fbldr: Option<DocBldFn>
) {
}

If you need a typed None around, you can declare a const one:

pub fn mktbldoc<H, B, F>(hbldr: Option<H>, bbldr: B, fbldr: Option<F>) -> Doc
where
  H: Fn(&mut sidoc::Builder),
  B: Fn(&mut sidoc::Builder),
  F: Fn(&mut sidoc::Builder)
{
   todo!()
}

// the new part
const NOP: Option<fn (&mut sidoc::Builder)> = None;

// ...
mktbldoc(NOP, |foo| {}, NOP);
2 Likes

Imo even better than @quinedot’s solution is getting a zero-sized option type (by using an uninhabited function type). You can do something like this:

#[inline]
pub fn nop() -> Option<impl Fn(&mut sidoc::Builder)> {
    enum Void {}
    None::<Void>.map(|void| move |_: &mut sidoc::Builder| match void {})
}
println!("{}", std::mem::size_of_val(&NOP));   // -> 8
println!("{}", std::mem::size_of_val(&nop())); // -> 0

(playground)


Edit 2022: This code will need slight modification to get the same zero-sized option behavior in edition 2021.

5 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.