c3c icon indicating copy to clipboard operation
c3c copied to clipboard

Creating struct and adding interface does not raise an error if interface is not implemented

Open joshring opened this issue 1 month ago • 9 comments

This struct does not implement Printable, but not error is raised

module test;
import std::io;

struct Example (Printable)
{
    ushort row;
    ushort offset;
    ushort col;
}

fn void main()
{
    Example ex;
    io::printn(ex); 
}

joshring avatar Nov 23 '25 22:11 joshring

That's because both the Printable methods are marked as @optional (https://github.com/c3lang/c3c/blob/master/lib/std/io/formatter.c3#L7-L11) so you can choose which one to implement. It does come with the side effect of not requiring either to be implemented though.

Book-reader avatar Nov 23 '25 22:11 Book-reader

Ah I see, feel free to close this if that's intensional behaviour. Perhaps verifying at least one thing in the interface is implemented would be a nice solution?

joshring avatar Nov 24 '25 00:11 joshring

That is not necessarily correct though, is it?

lerno avatar Nov 24 '25 11:11 lerno

Unless there was some additional constraint for interfaces that have a way to explain that one or both methods need to be implemented. I think this is rare.

lerno avatar Nov 24 '25 11:11 lerno

That is not necessarily correct though, is it?

Intuitively you would need at least one thing to implement an interface, but the problems come when you need 2 out of 4 options that's tricky

joshring avatar Nov 24 '25 13:11 joshring

Should I close this?

lerno avatar Nov 26 '25 23:11 lerno

One suggestion might be some sort of contract on the interface where such things could be specified

joshring avatar Nov 28 '25 09:11 joshring

for one option or another

<*
 @optional_sets {{to_constant_string}, {to_format}}
*>
interface Printable
{
	fn String to_constant_string() @optional;
	fn usz? to_format(Formatter* formatter) @optional;
}

Either example1 and example2 or example2 and example3

<*
 @optional_sets {{example1, example2}, {example2, example3}}
*>
interface Example
{
	fn String example1() @optional;
	fn String example2() @optional;
	fn String example3() @optional;
}

joshring avatar Dec 01 '25 12:12 joshring

you could also theoretically reuse @require, but without a context-sensitive keyword to refer to the type implementing the interface it could be a bit of a hack:

<*
 @require $defined({}.to_constant_string) ||| $defined({}.to_format) : "You must implement either to_constant_string or to_format"
*>
interface Printable
{
	fn String to_constant_string() @optional;
	fn usz? to_format(Formatter* formatter) @optional;
}

or

<*
 @require $defined(implementer.to_constant_string) ||| $defined(implementer.to_format) : "You must implement either to_constant_string or to_format"
*>
interface Printable
{
	fn String to_constant_string() @optional;
	fn usz? to_format(Formatter* formatter) @optional;
}

probably not as good as @joshring's idea, but just throwing an idea out there.

Book-reader avatar Dec 03 '25 00:12 Book-reader