I am having difficulty understanding the relationship between interfaces and structs in go. I have declared an interface called Datatype as follows:
package main
type Datatype interface {
Unmarshal(record []string) error
String() string
}
I have also created several structs that implement this interface. Here is one simple example:
package main
import (
"encoding/csv"
"fmt"
"gopkg.in/validator.v2"
"reflect"
"strconv"
"time"
)
type User struct {
Username string `validate:"nonzero"`
UserId string `validate:"nonzero"`
GivenName string `validate:"nonzero"`
FamilyName string `validate:"nonzero"`
Email string `validate:"regexp=^[0-9a-zA-Z]+#[0-9a-zA-Z]+(\\.[0-9a-zA-Z]+)+$"`
SMS string `validate:"nonzero"`
Phone string `validate:"min=10"`
DateOfBirth time.Time
}
type Users []User
func (u *User) Unmarshal(record []string) error {
s := reflect.ValueOf(u).Elem()
if s.NumField() != len(record) {
return &FieldMismatch{s.NumField(), len(record)}
}
for i := 0; i > s.NumField(); i++ {
f := s.Field(i)
switch f.Type().String() {
case "string":
f.SetString(record[i])
case "int", "int64":
ival, err := strconv.ParseInt(record[i], 10, 0)
if err != nil {
return err
}
f.SetInt(ival)
default:
return &UnsupportedType{f.Type().String()}
}
}
return nil
}
func (u *User) String() string {
return fmt.Sprintf("%#v", u)
}
func (u *User) populateFrom(reader *csv.Reader) (Users, error) {
var users Users
for {
record, err := reader.Read()
check(err)
err = u.Unmarshal(record)
check(err)
valid := validator.Validate(u)
if valid == nil {
user := *u
users = append(users, user)
} else {
fmt.Println("Validation error?: ", valid)
}
}
return users, nil
}
Problem:
As you can see, I also have a type called Users which is just []User. When I try to return this type from a function that has a return type of []Datatype, I get the following error message:
cannot use results (type Users) as type []Datatype in return argument
I'm sure I'm missing something obvious but it seems to me that this should work.
Question:
Could someone please explain why it does not work? Is there a better (more idiomatic) way to achieve this end result?
Slices are not covariant; even though User implements Datatype, []User does not implement []Datatype (because nothing implements []Datatype: it itself is not an interface type, it's just a slice type whose element type is an interface type).
Edited to add: As Dave C points out in a comment above, a closely-related question appears in the Go FAQ. [link] The Go FAQ is licensed in a way that's compatible with Stack Exchange content, so, here's the question in its entirety:
Can I convert a []T to an []interface{}?
Not directly, because they do not have the same representation in memory. It is necessary to copy the elements individually to the destination slice. This example converts a slice of int to a slice of interface{}:
t := []int{1, 2, 3, 4}
s := make([]interface{}, len(t))
for i, v := range t {
s[i] = v
}
Related
I have a type with three fields
type Person struct {
FirstName string
LastName string
Age int
}
Creating an instance and using default fmt.Sprint() returns {John Smith 45}. However for my use case I need a string of format John, Smith, 45. A comma delimited list without being surrounded by curly braces. Is there a more reusable and effective way than:
fmt.Sprintf("%s, %s, %d", x.FirstName, x.LastName, x.Age)
I will be using this method alot with other types and I would prefer a generic method rather than having to type out a format for each type I use:
func asFields(data interface{}) string {
// TODO logic here
}
That exact format is not supported by verbs of the fmt package.
The closest would be
s := fmt.Sprintf("%#v", p)
Which generates a string like:
main.Person{FirstName:"John", LastName:"Smith", Age:45}
If you exactly need what you posted in the question, you may use reflection to iterate over the fields and build the result like this:
func asFields(data interface{}) string {
v := reflect.ValueOf(data)
b := &strings.Builder{}
for i := 0; i < v.NumField(); i++ {
if i > 0 {
b.WriteString(", ")
}
b.WriteString(fmt.Sprint(v.Field(i).Interface()))
}
return b.String()
}
This indeed gives:
John, Smith, 45
Try the examples on the Go Playground.
Note that this asFields() function handles all struct types of course not just your Person. Adjustment would be needed to handle pointers and struct of structs of course.
Also note that alternatively to fmt.Sprint() you may also use fmt.Fprint() directed to the buffer in which we're assembling the string:
func asFields(data interface{}) string {
v := reflect.ValueOf(data)
b := &strings.Builder{}
for i := 0; i < v.NumField(); i++ {
if i > 0 {
b.WriteString(", ")
}
fmt.Fprint(b, v.Field(i).Interface())
}
return b.String()
}
Which of course gives the same result (and may or may not be faster, benchmark it). Try it on the Go Playground.
Can we use this approach, it will be applicable only to Person type struct though ?
https://play.golang.org/p/YI2Nu0q51ls
package main
import (
"fmt"
)
type Person struct {
FirstName string
LastName string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%s, %s, %d", p.FirstName, p.LastName, p.Age)
}
func main() {
p := Person{FirstName: "John",LastName: "Doe", Age: 25}
fmt.Printf("%v", p)
}
Output:
John, Doe, 25
Similar SO question:
ToString() function in Go
I'm using to fmt.Sscan convert a string to any type, here is what I'm doing:
package main
import (
"fmt"
"reflect"
)
func test() interface{} {
return 0
}
func main() {
a := test() // this could be any type
v := "10" // this could be anything
fmt.Println(reflect.TypeOf(a), reflect.TypeOf(&a))
_, err := fmt.Sscan(v, &a)
fmt.Println(err)
}
This code is failing because Sscan doesn't accept interfaces as the second value: can't scan type: *interface {}. demo
What I find most weird is that the first print prints: int *interface {}, is it a int or an interface?
How can I assert a to the right type (it could be any primitive)? Is there a solution that doesn't include a giant switch statement?
Thank you.
Here's how to convert a string to a value of any type supported by the fmt package:
// convert converts s to the type of argument t and returns a value of that type.
func convert(s string, t interface{}) (interface{}, error) {
// Create pointer to value of the target type
v := reflect.New(reflect.TypeOf(t))
// Scan to the value by passing the pointer SScan
_, err := fmt.Sscan(s, v.Interface())
// Dereference the pointer and return the value.
return v.Elem().Interface(), err
}
Call it like this:
a := test()
a, err := convert("10", a)
fmt.Println(a, err)
Run it on the Playground
What I want to do is:
1. Parse model from url parameter in endpoint.(ex: media, account)
mysite.com/v1/rest/:model <- :model can be whether 'media', 'account'.
So it will look like:
mysite.com/v1/rest/media
mysite.com/v1/rest/account
2. Use 1, retrieve string and use it for getting corresponding struct.
3. Put it to the method which takes interface{}
My code looks like:
type Media struct {
Caption string
}
type Account struct {
Bio string
}
type AdminController struct {
TableName string
ID int64
}
func (c *AdminController) Get(n *core.Network) {
// I want to put struct to below GetModels method dynamically.
// Not explicitly like this.
total, data, err := c.GetModels(&Media{}, n)
// I want to do this
total, data, err := c.GetModels(caster("media"), n)
if err != nil {
n.Res.Error(err)
} else {
n.Res.Success(total, data)
}
}
Is it possible to implement method which takes string and return corresponding struct? Like this:
func (c *AdminController) caster(model string) interface{} {
if string == "media" {
return &Media{}
} else if string == "account" {
return &Account{}
}
return nil
}
If it's possible, is this a good way to deal with REST request (generic way) Or should I implement response methods one by one following table by table?
If I have to implement REST(4) * number_of_tables methods, it doesn't seem to be efficient. Any advice on this architectural problem will be appreciated.
currently working with the vishvananda/netns package trying to extract routes from a specific network namespace.
There is a defined Handle struct which is returned when I request a 'handle' for a specific network namespace. As such:
func NewHandleAt(ns netns.NsHandle, nlFamilies ...int) (*Handle, error)
This is then a receiver argument (?) to a function that requires that handle,
func (h *Handle) LinkList() ([]Link, error)
I'm new to go and not sure how to tie these together. I'm stuck with:
func (h *Handle) showInts() {
int, err := h.netlink.LinkList()
if err != nil {
log.Fatal(err)
}
for i, r := range int {
log.Printf("%d: %s", i, r.Attrs().Name)
}
}
func main() {
ints, err := netlink.LinkList()
if err != nil {
log.Fatal(err)
}
for i, r := range ints {
log.Printf("%d: %s", i, r.Attrs().Name)
}
pid, err := netns.GetFromPid(9097)
if err != nil {
log.Fatal(err)
}
netlink.NewHandleAt(pid)
showInts()
}
Update
While writing the original answer, touched on a number of things, without any clear structure, so here's a more structured version:
Depending on what you're actually asking (ie "How do I add a receiver function/method to an exported type", or "What the hell is a receiver function"), the answers are as follows:
How do I add a receiver function to an exported type?
Easy, same as you do with any other type. You were close, in fact. This doesn't work:
func (h *Handler) showInts() {}
Because you're adding a method to the Handler type in your package. Given you have a main function, that would be the main package. You're trying to add it to the netlink.Handler type instead. In which case, this will work:
func (h *netlink.Handler) showInts(){}
The type is netlink.Handler in your main package after all... This, however will not work. The compiler will refuse to compile, telling you: "Cannot define new methods on non-local type". This is easily mitigated, though, by creating a new type, and add the method there:
type MyHandler netlink.Handler
func (h *MyHandler) showInts(){}
Be that as it may, the last 2 lines in your code strike me as wrong.
Given that NewHandleAt returns (*Handle, error), and netlink.Handle is a receiver argument, the correct way would be:
var mh *MyHandle
if h, err := netlink.NewHandleAt(pid); err != nil {
log.Fatal(err) // something went wrong
} else {
mh = (*MyHandle)(h)
}
mh.showInts() // call showInts on mh, which is of type *MyHandle
The fact that you've "wrapped" the external type in a custom type does mean you'll find yourself casting the same thing quite a lot. Say netlink.Handle has a Test method, and you want to call it inside showInts:
func (h *MyHandle) showInts() {
nh := (*netlink.Handle)(h) //cast required
nh.Test()
}
I'd also change the varname from pid to nsh or something, because it's a NsHandle, and not a pid after all...
What is a receiver argument?
Because you wrote this:
This is then a receiver argument (?) to a function that requires that handle,
I get the impression you're not entirely clear on what a receiver argument is. Put simply, it's like a function argument, but instead of an argument that is just passed to a function, it's an argument that holds the object/value on which the function is called. Basically, it's the "instance" on which the function/method is called. Think of it as the this keyword in many OOP languages:
func (h *MyHandle) showInts() {
return
}
In something like C++ would be
class MyHandle : Handle
{
public:
void showInts(void) { return; } // replace h with this
}
There are significant differences, however:
The receiver argument can be a pointer, or a value - in case of a value receiver, the method cannot modify the receiver value
There's no such thing as private, public, or protected... at least not in the traditional OO way
...
There's quite a few differences, perhaps consider going through the golang tour. The stuff about go methods can be found here
Other issues/weird things
After looking at your code again, I'm really not sure whether this is correct:
h.netlink.LinkList()
In your main function, you call netlink.LinkList(). h is a *netlink.Handler. If you need to call the netlink.LinkList function, it's highly likely h.netlink.LinkList is not what you want to do. Instead, you should simply call netlink.LinkList().
That's assuming you need to call the function in the first place.
Given that you've already called it in the main function, why not pass it as an argument?
//in main:
ints, err := netlink.LinkList()
//...
h.showInts(ints)
func (h *MyHandle)showInts(ll []netlink.Link) {
}
Thanks Elias, awesome answer!
From that, I've written the following code which will list interfaces belonging to a specific namespace. Thanks!
package main
import (
"github.com/vishvananda/netns"
"github.com/vishvananda/netlink"
"log"
)
type NSHandle netlink.Handle
func (h *NSHandle) showInts() {
nh := (*netlink.Handle)(h) //cast required
int, err := nh.LinkList()
if err != nil {
log.Fatal(err)
}
log.Printf("Namespace Ints:")
for i, r := range int {
log.Printf("%d: %s", i, r.Attrs().Name)
}
}
func getNSFromPID(pid int) (*NSHandle) {
hpid, err := netns.GetFromPid(9115)
if err != nil {
log.Fatal(err)
}
var nsh *NSHandle
if h, err := netlink.NewHandleAt(hpid); err != nil {
log.Fatal(err) // something went wrong
} else {
nsh = (*NSHandle)(h)
}
return nsh
}
func main() {
getNSFromPID(9115).showInts()
}
I'm working with json and golang. I've made a TCP server and, I need to Unmarshal the message to know which type of service is asked, before Unmarshal the contained data. It's a bit hard to explain so this is my code:
package main
import (
"fmt"
"encoding/json"
)
type Container struct {
Type string
Object interface{}
}
type Selling struct {
Surname string
Firstname string
//......
Price int
}
type Buying struct {
ID int
Surname string
Firstname string
//..........
}
/*
type Editing struct {
ID int
...............
}
Informations, etc etc
*/
func main() {
tmp_message_json1 := Selling{Surname: "X", Firstname: "Mister", Price: 10}
//tmp_message_json1 := Buying{ID: 1, Surname: "X", Firstname: "Mister"}
tmp_container_json1 := Container{Type: "Selling", Object: tmp_message_json1}
json_tmp, _ := json.Marshal(tmp_container_json1)
/*...........
We init tcp etc etc and then a message comes up !
...........*/
c := Container{}
json.Unmarshal(json_tmp, &c)
//I unmarshal a first time to know the type of service asked
// first question: Does Unmarshal need to be used only one time?
// does I need to pass c.Object as a string to unmarshal it in the called functions?
if c.Type == "Buying" {
takeInterfaceBuying(c.Object)
} else if c.Type == "client" {
takeInterfaceSelling(c.Object)
} else {
fmt.Println("bad entry")
}
}
func takeInterfaceBuying(Object interface{}) {
bu := Object
fmt.Println(bu.Firstname, bu.Surname, "wants to buy the following product:", bu.ID)
}
func takeInterfaceSelling(Object interface{}) {
se := Object
fmt.Println(se.Firstname, se.Surname, "wants to sell something for:", se.Price)
}
I need to handle messages which comes up like it, but I don't know if it's possible? does it possible?
Thank for help !
There is json.RawMessage for this purpose.
package main
import (
"encoding/json"
"fmt"
)
type Container struct {
Type string
Object json.RawMessage
}
type Selling struct {
Surname string
Firstname string
Price int
}
type Buying struct {
ID int
Surname string
Firstname string
}
func main() {
rawJson1 := []byte(`{"Type":"Selling","Object":{"Surname":"X","Firstname":"Mister","Price":10}}`)
rawJson2 := []byte(`{"Type":"Buying","Object":{"ID":1,"Surname":"X","Firstname":"Mister"}}`)
processMessage(rawJson1)
processMessage(rawJson2)
}
func processMessage(data []byte) {
var c Container
json.Unmarshal(data, &c)
switch {
case c.Type == "Buying":
processBuying(c)
case c.Type == "Selling":
processSelling(c)
default:
fmt.Println("bad entry")
}
}
func processBuying(c Container) {
var bu Buying
json.Unmarshal(c.Object, &bu)
fmt.Println(bu.Firstname, bu.Surname, "wants to buy the following product:", bu.ID)
}
func processSelling(c Container) {
var se Selling
json.Unmarshal(c.Object, &se)
fmt.Println(se.Firstname, se.Surname, "wants to sell something for:", se.Price)
}
I might be wrong, but I don't think you can do it in one step.
First idea: Unmarshal in map[string]interface{}
Don't use type with unmarshal, instead use a map[string]interface{}, and then construct Selling/Buying from this map (or use directly the map)
type Container struct {
Type string
Object map[string]interface{}
}
Second idea: Two steps / clueless container
First: Unmarshall in a clueless Container that doesn't know the type
type CluelessContainer struct {
Type string
Object interface{} `json:"-"` // or just remove this line ?
}
Then unmarshall in the type aware container. You could use a factory pattern to come with the right struct.