Compilation error when an `unimplemented!()` might be encountered?


#1

Howdy,
Let’s say I’m implementing a trait, but one of more of the functions is unimplemented!() for my struct, because, let’s say, it makes no sense in this particular case. However, I’d like to safeguard myself and others who use my library from a case in which they call this function in some edge case.
Can I put something in my trait implementation for this method, similar to unimplemented!(), but that instead would check during compilation whether anywhere in the codebase this function is called from this struct?

So, this should compile just fine:

impl MyTrait for MyStruct {
    fn a() { println!("yay!"); }
    fn b() { unimplemented!() }
}

fn main() {
    MyStruct::a();
    MyStruct::a();
}

But this would cause a compilation error:

impl MyTrait for MyStruct {
    fn a() { println!("yay!"); }
    fn b() { unimplemented!() }
}

fn main() {
    MyStruct::a();
    MyStruct::b();   // Compilation error here, e.g. "This will panic in runtime under some conditions"
    MyStruct::a();
}

#2

Not aware of anything like that. If a feature like that existed, it would need to work through generics and more crucially, type erasure which basically makes it impossible.

Do you have control over the trait? Can you use a different trait? The real problem is either MyTrait or MyStruct is defined incorrectly.


#3

This thread on internals was related:


#4

I guess this could be done with supertraits, having MyStruct implement the supertrait with the partial functionality that fits all cases and extend it. But that would be punishing the much more common scenario when all the functions need to be implemented, because then for all other structs there would have to be two impls.


#5

I actually tried it, and it’s not exactly what I need. It just doesn’t compile incomplete code, whether it’s actually used or not.


#6

This sort of works: https://crates.io/crates/dont_panic
But the error message is dirty and there are some other caveats.


#7

You can do it with a special bound on b():

trait MyTrait {
   fn a();
   fn b() where Self: MyMarker;
}

trait MyMarker {}

Impls still need to define b() but it’s not callable unless they impl MyMarker too. MyMarker then becomes the toggle for dis/allowing the call.

This still needs a second trait but takes advantage of standard language features.