'optional' parameter and monomorphization

I have a (trait) function like this, which copies data from a 2d map (self) to another 2d map (dst).

fn copy_rect<DstMap>(&self, dst: &mut DstMap, src_rect: &Rect, dst_point: &Point)
where
    DstMap: MapTrait<T>
{
    // do my thing
}

Now I would like to have a version which uses a clip_rect (avoid drawing outside this rect).
Of course I can duplicate the code with an extra parameter.
But I was wondering if it is possible to change the function and add a clip_rect: Option<&Rect> parameter. With the extra requirement that it will be monomorphized. Performance is quite important.

fn copy_rect<DstMap>(&self, dst: &mut DstMap, src_rect: &Rect, dst_point: &Point, 
    clip_rect: Option<&Rect>)
where
    DstMap: MapTrait<T>
{
    if let Some(clip) = clip_rect
    {
        // do some extra clipping 
    }
    // do my thing
}

When I call the function like this:

    map.copy_rect(dst, src_rect, dst_point, None);

I guess the compiler is smart enough?

Anyway: what would be the smartest / most performant way to do this without creating an extra function?

Rust does not support any kind of argument-based function overloading: you can't have two functions with the same name. What you can have is a "shortcut" function that invokes the most complete one under a different name. E.g.,

fn copy_rect<DstMap>(&self, dst: &mut DstMap, src_rect: &Rect, dst_point: &Point)
where
    DstMap: MapTrait<T>
{
    copy_rect_clip(self, dst, src_rect, dst_point, None);
}

fn copy_rect_clip<DstMap>(&self, dst: &mut DstMap, src_rect: &Rect, dst_point: &Point, 
    clip_rect: Option<&Rect>)
where
    DstMap: MapTrait<T>
{
    if let Some(clip) = clip_rect
    {
        // do some extra clipping 
    }
    // do my thing
}

Given that you cannot use the same name for the two function you can also just move the common code into a third, private function and have a better (IMHO) API:

pub fn copy_rect<DstMap>(&self, dst: &mut DstMap, src_rect: &Rect, dst_point: &Point)
where
    DstMap: MapTrait<T>
{
    copy_rect_impl(self, dst, src_rect, dst_point, None);
}

pub fn copy_rect_clip<DstMap>(&self, dst: &mut DstMap, src_rect: &Rect, dst_point: &Point, 
    clip_rect: &Rect)
where
    DstMap: MapTrait<T>
{
    copy_rect_impl(self, dst, src_rect, dst_point, clip_rect);
}

fn copy_rect_impl<DstMap>(&self, dst: &mut DstMap, src_rect: &Rect, dst_point: &Point, 
    clip_rect: Option<&Rect>)
where
    DstMap: MapTrait<T>
{
    if let Some(clip) = clip_rect
    {
        // do some extra clipping 
    }
    // do my thing
}

But in this I'd probably just go with a single function with the extra Option parameter.

But in this I'd probably just go with a single function with the extra Option parameter

Agree. I believe when calling the function with None as parameter (so known at compiletime) it will be optimized anyway. (I don't know much about how to verify this).

It depends. The compiler is free to do it either way, depending on the function and how it's called. The Option could be optimized away if the function is inlined, for example. I think you will have to inspect the assembly to know for sure.

One way to hint to the compiler is to make the function with the optional parameter call another function for the "actual work":

fn copy_rect<DstMap>(&self, dst: &mut DstMap, src_rect: &Rect, dst_point: &Point, 
    clip_rect: Option<&Rect>)
where
    DstMap: MapTrait<T>
{
    if let Some(clip) = clip_rect
    {
        // do some extra clipping 
    }
    
    // Assumes that clipping is already applied, somehow.
    // Also an opportunity to convert DstMap to something specific.
    self.copy_rect_clipped(dst, src_rect, dst_point);
}

This makes the outer function smaller, which may increase the chance of inlining. #[inline] may help as extra encouragement.

As for performance, that also depends. Inlining reduces the number of function calls, but also increases the calling function's size. I can't give a rule of thumb, but I understand that instruction caches may matter at some point. You will have to measure different options to know for sure.

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.