V: interface error with a struct type

Created on 6 Apr 2019  Â·  10Comments  Â·  Source: vlang/v

Trying to use interface to create a generic toString() function, I got a bug error:

struct Point {
    x int
    y int
}

fn (p Point) draw() string {
    return 'Point(${p.x},${p.y})'
}

fn toString(d Drawable) string {
    return d.draw()
}

interface Drawable { 
        draw() string 
}

fn main() {
    p := Point{x: 2, y: 3}
    println(p.draw())
}
Bug

All 10 comments

Thanks, I know what's causing this. Will try to fix today.

You can fix it if you change the order.

struct Point {
    x int
    y int
}

fn (p Point) draw() string {
    return 'Point(${p.x},${p.y})'
}

interface Drawer { 
        draw() string 
}

fn to_string(d Drawer) string {
    return d.draw()
}


fn main() {
    p := Point{x: 2, y: 3}
    println(p.draw())
}

Fixes problem.

With current master the only restriction is that interfaeces have to end with _er_. The order thing has been fixed. And functions have to be in snake case by language definition that's all.

@medvednikov : IMHO this issue can be closed or splitted because of the _er_ restriction for interface names.

struct Point {
    x int
    y int
}

fn (p Point) draw() string {
    return 'Point(${p.x},${p.y})'
}

fn to_string(d Drawer) string {
    return d.draw()
}

interface Drawer { 
        draw() string 
}

fn main() {
    p := Point{x: 2, y: 3}
    println(p.draw())
}

You can fix it if you change the order.

struct Point {
  x int
  y int
}

fn (p Point) draw() string {
  return 'Point(${p.x},${p.y})'
}

interface Drawer { 
        draw() string 
}

fn to_string(d Drawer) string {
  return d.draw()
}


fn main() {
  p := Point{x: 2, y: 3}
  println(p.draw())
}

Fixes problem.

With current version (V 0.1.21 c7e47e6), replacing

println(p.draw())

with

println(to_string(p))

gives this:

Point(-354928136,32766)

You can fix it if you change the order.

struct Point {
    x int
    y int
}

fn (p Point) draw() string {
    return 'Point(${p.x},${p.y})'
}

interface Drawer { 
        draw() string 
}

fn to_string(d Drawer) string {
    return d.draw()
}


fn main() {
    p := Point{x: 2, y: 3}
    println(p.draw())
}

Fixes problem.

With current version (V 0.1.21 c7e47e6), replacing

println(p.draw())

with

println(to_string(p))

gives this:

Point(-354928136,32766)

And why is this wrong? It can't function this way. fn to_string(d Drawer) string{...} awaits an interface not an actual struct. This can't work as v is designed different as p is not a classic object which implements an interface thus you can access methods through them.

The docs say this:

A type implements an interface by implementing its methods. There is no explicit declaration of intent, no "implements" keyword.

So it can't work because only p implements draw(). The interface in to_string(...){...} has access to a random memory area. It should throw an error if you do that,

@medvednikov : BDFL, what do you think about that?

In my understanding, the very idea of the interface is to be able to define different structures with certain methods, required by the interface, and then be able to supply these structures in any place where this interface can be used. to_string() definition tells us that it expects a parameter that has interface Drawer implemented (and that means the parameter should have method draw() string defined). So, any stucture with the defined draw() string method could be used as a parameter for to_string(s Drawer).
@M4SSD35TRUCT10N What is your idea of the interface then? What it is for?
What for are interface Drawer { ...} and fn to_string(d Drawer) string {...} in the code above if we do not use them at all?

In my understanding, the very idea of the interface is to be able to define different structures with certain methods, required by the interface, and then be able to supply these structures in any place where this interface can be used. to_string() definition tells us that it expects a parameter that has interface Drawer implemented (and that means the parameter should have method draw() string defined). So, any stucture with the defined draw() string method could be used as a parameter for to_string(s Drawer).
@M4SSD35TRUCT10N What is your idea of the interface then? What it is for?
What for are interface Drawer { ...} and fn to_string(d Drawer) string {...} in the code above if we do not use them at all?

To be honest I must have been drunk or blind or both... I got confused by that chained return of the string. Of course that should work. I rewrote it like this to be more like the example in the docs and it still produces nonsense (seems that when using a struct with actual content doesn't work.

struct Point {
    x i8 
    y i8
}

fn (p Point) draw() string {
    return 'Point(${p.x},${p.y})'
}

fn to_string(d Drawer) {
    println(d.draw())
}

interface Drawer { 
        draw() string 
}

fn main() {
    p := Point{x: 2, y: 3}
    to_string(p)
}

EDIT
But this works.

struct Point {
    x i8 
    y i8
        z i8
}

fn (p Point) draw() string {
    return 'Point(${p.x},${p.y})'
}

fn to_string(d Drawer) {
    println(d.draw())
}

interface Drawer { 
        draw() string 
}

fn main() {
    p := Point{x: 2, y: 3}
    to_string(p)
}

It prints: Point(2,3).
You can even do this with initializing the third element of the struct. I'll take a look into the generated C code.

EDIT 2
This variant also works:

struct Point {
    x int
    y int
        z int
}

fn (p Point) draw() string {
    return 'Point(${p.x},${p.y})'
}

fn to_string(d Drawer) string {
    return d.draw()
}

interface Drawer { 
        draw() string 
}

fn main() {
    p := Point{x: 2, y: 3}
    println(to_string(p))
}

It has to do with the quantity of the elements in a struct when need to stringifiy them. Everything below 3 doesn't work. Within the temporary C files (by adding -show_c_cmd -debug) is nothing totally strange to see. Too bad I'm not as good in C as needed at this stage. If you use string elements your example works fine.

The error with the latest V:

==================
/tmp/v/foo.14040655549332756735.tmp.c: In function ‘main__to_string’:
/tmp/v/foo.14040655549332756735.tmp.c:8066:9: error: ‘main__Drawable_name_table’ undeclared (first use in this function)
 8066 |  return main__Drawable_name_table[d._interface_idx].draw(d._object);
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~
/tmp/v/foo.14040655549332756735.tmp.c:8066:9: note: each undeclared identifier is reported only once for each function it appears in
/tmp/v/foo.14040655549332756735.tmp.c:8067:1: warning: control reaches end of non-void function [-Wreturn-type]
 8067 | }
      | ^
...
==================
(Use `v -cg` to print the entire error message)

builder error: 
==================
C error. This should never happen.

If you were not working with C interop, please raise an issue on GitHub:

https://github.com/vlang/v/issues/new/choose

You can also use #help on Discord: https://discord.gg/vlang

Fixed in 3b94a2b (I've added all the variants as separate _test.v files under vlib/v/tests/interface_edge_cases/ ).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jtkirkpatrick picture jtkirkpatrick  Â·  3Comments

penguindark picture penguindark  Â·  3Comments

cjmxp picture cjmxp  Â·  3Comments

arg2das picture arg2das  Â·  3Comments

XVilka picture XVilka  Â·  3Comments