Two Default Impls but at least one must be Impl'ed

I was playing with some Trait design and wondered if the compiler would stop me from doing this. Is there a way to flag that at least one of these Traits must be impl'ed? Basically, I want my StupidImpl to be forbidden by the compiler, but it is not and it generates a stack overflow.

pub trait Transmit {
    fn transmit(&self, word: u8){
        let buf = [word];
        self.transmit_buffer(&buf, 1);
    }
    fn transmit_buffer(&self, buf: &[u8], len: usize){
        for i in 0..len {
            self.transmit(buf[i]);
        }
    }
}

#[cfg(test)]
mod tests {
    // example of client that impls buffer only
    struct Foo;
    use Transmit;
    impl Transmit for Foo {
        fn transmit_buffer(&self, buf: &[u8], len: usize){
            for i in 0..len {
                print!("{}", buf[i]);
            }
        }
    }

    #[test]
    fn test_buffer_but_no_byte() {
        let buf = [1, 2, 3];
        let foo = Foo {};
        foo.transmit_buffer(&buf, buf.len());
        println!();
    }

    // example of client that impls byte mode only
    struct Bar;
    impl Transmit for Bar {
        fn transmit(&self, word: u8){
            print!("{}", word);
        }
    }

    #[test]
    fn test_byte_but_no_buffer() {
        let buf = [1, 2, 3];
        let bar = Bar {};
        bar.transmit_buffer(&buf, buf.len());
        println!();
    }

    // example of client that impls none
    struct StupidImpl;
    impl Transmit for StupidImpl {
    }

    #[test]
    fn test_no_byte_no_buffer() {
        let buf = [1, 2, 3];
        let bar = StupidImpl {};
        bar.transmit_buffer(&buf, buf.len());
        println!();
    }
}

(Playground)

Output:


running 3 tests
test tests::test_buffer_but_no_byte ... ok
test tests::test_byte_but_no_buffer ... ok

Errors:

   Compiling playground v0.0.1 (file:///playground)
    Finished dev [unoptimized + debuginfo] target(s) in 1.77s
     Running target/debug/deps/playground-c3c1b3966c1538ae

thread 'tests::test_no_byte_no_buffer' has overflowed its stack
fatal runtime error: stack overflow
error: process didn't exit successfully: `/playground/target/debug/deps/playground-c3c1b3966c1538ae` (signal: 6, SIGABRT: process abort signal)

There's a long-standing issue open about this:

https://github.com/rust-lang/rfcs/issues/628

2 Likes

Thanks for that!