structvlang

How can i add methods to a struct in vlang?


I am quite new to the v language and i discovered this feature, however it seems like i am doing something wrong.

I have a struct that gathers information in case an error is found somewhere and i'd like to create a function in the same structure to return a string containing all the values so that i can call it and store it in a variable the proper way: error_msg := error.to_string()

I am using v version 0.3.3

Having the Error struct:

pub struct Error {
    pub:
        text    string
        row    int
        col    int
        complementary    string
}

I have tried:

fn to_string(e Error) string {
    return e.text+'\n in row '+ e.row+' at col '+ e.col+'\n'+e.complementary
}
fn (e Error) to_string() string {
    return e.text+'\n in row '+ e.row+' at col '+ e.col+'\n'+e.complementary
}

I would like the second option to work but it is outputing the following error while compiling it:

structs/error.v:11:7: error: cannot define new methods on non-local type Error
    9 | }
   10 |
   11 | fn (e Error) to_string() string {
      |       ~~~~~
   12 |     return e.text+'\n in row '+ e.row+' at col '+ e.col+'\n'+e.complementary
   13 | }

The function and the struct are in the same module as expected and even in the same file Full code

module structs

pub struct Error {
    pub:
        text    string
        row    int
        col    int
        complementary    string
}

fn (e Error) to_string() string {
    return e.text+'\n in row '+ e.row+' at col '+ e.col+'\n'+e.complementary
}

That being said, it is going to be called in another file and module:

pub fn read(file_name string) (int, string) {

    data := read_file(file_name) or {
        error_str := Error{text: 'File "$file_name" does not exist', row: 2, col: 0}
            return 1, error_str.to_string()
    }
    return 0, data
}

Solution

  • The error cannot define new methods on non-local type Error occurs because Error is a V builtin, thus already defined (non-locally), and you cannot add a new method to that here.

    Also, you're getting this error on the method definition because it presumably precedes the struct's definition in your source code. If you flipped their order of definition (struct first, then the method), the issue wouldn't change but the error message would have been more to the point, reading: cannot register struct `Error`, another type with this name exists.

    The quick fix

    Give your struct a new name:

    struct MyError {
      text             string
      row              int
      col              int
      complementary    string
    }
    
    fn (e MyError) to_string() string {
      return e.text + '\n in row ' + e.row.str() + ' at col ' + e.col.str() + '\n' + e.complementary
    }
    
    error_str := MyError{text: 'File does not exist', row: 2, col: 0}
    println(error_str.to_string())
    
    File does not exist
     in row 2 at col 0
    
    A more idiomatic approach

    Define proper custom error types which you can then use as actual errors: Either manually implement the IError interface (requiring the two methods msg() string and code() int), or just embed the interface's default implementation Error (yes, the one you have accidentally been trying to shadow) which provides empty copies of both methods, so you only have to add what's needed. For instance:

    struct MyError {
      Error
      text             string
      row              int
      col              int
      complementary    string
    }
    
    fn (e MyError) msg() string {
      return '${e.text}\n in row ${e.row} at col ${e.col}\n${e.complementary}'
    }
    
    fn read(file_name string) !string {
      failed := true
      if failed {
        return MyError{text: 'File "${file_name}" does not exist', row: 2, col: 0}
      }
      return "data"
    }
    
    data := read("xy") or { panic(err) }
    println(data)
    
    V panic: MyError: File "xy" does not exist
     in row 2 at col 0
    
    v hash: 046dd54
    /tmp/v_1000/main.13380111000873620315.tmp.c:7817: at _v_panic: Backtrace
    /tmp/v_1000/main.13380111000873620315.tmp.c:17765: by main__main
    /tmp/v_1000/main.13380111000873620315.tmp.c:18160: by main