Method overloading and deduplication using @constCast

Yes - I brought up that example in the original post.

The BoundedArray example is an interesting case because the return type is mirrored based on the self type of it’s slice function. In practice that looks like…

f(*T) -> []U and f(*const T) -> []const U

However, bounded array has a subtle but important difference because the return statement automatically binds the const qualifier due to the return type. It isn’t an explicit @constCast. It also doesn’t internally dispatch recursively and strip away a const qualifier.

I’m willing to believe in the safety of this method because you can call const methods functions in non-const methods (it just appends the qualifier to the pointer).

So one case this could be handy is in the case of dependent getters… say I have a getter that returns the last item appended to a list… we’ll call it last and we can write a helper function here to cleanup returns.

fn MatchPtrType(comptime Parent: type, comptime Child: type) type {
    return if (comptime isConstPtr(Parent)) *const Child else *Child;
}

// @constCast can propgate through optional pointers... ?*const T -> ?*T

pub fn last(self: anytype) ?MatchPtrType(@TypeOf(self), Node) {
    if (comptime isConstPtr(@TypeOf(self))) {
         
        return if (self.items.len > 0)
            &self.items[self.items.len - 1] else null;
         
    } else {
        return @constCast(asConst(@This(), self).last());
    }
}  

As the implementation of the function itself gets more complex, this option because more appealing (say for instance if we needed to look up a node in a tree), then this becomes particularly useful.

However, it can be easily abused too. First, we should never go in the opposite direction - don’t ever cast down from const to call a non-const function. That could cause the optimizer to botch things quite badly - cast up and then back down.

Second, I’d say it’s best to not append additional code to the casting branch - that actually creates separate functions.

As far as I can see up to this point, the most appropriate use case for this is to swap out constness on returned pointers or pointer-like types (like slices). Thanks to that, we can overload our methods.

2 Likes