Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unexpected return of anonymous struct

Tags:

struct

go

I am trying to implement a method that returns a modified struct based on the original one, such as:

type Project struct {
    Username string           
    Id       uint      
    Alias    string           
    Data     *json.RawMessage 
    Scheme   Scheme          
}

func (p *Project) OmitUsername() *struct {

    return &struct {
        Id      uint         
        Alias   string   
        Data    *json.RawMessage
        Scheme  Scheme          
    }{
        p.Id,
        p.Alias,
        p.Data,
        p.Scheme
    })
}

And I get the following error:

models/project.go:22: syntax error: unexpected return 
models/project.go:24: non-declaration statement outside function body 
models/project.go:25: non-declaration statement outside function body 
models/project.go:25: syntax error: unexpected string literal, expecting semicolon or newline 
models/project.go:26: non-declaration statement outside function body

Any help would be appreciated.

like image 764
Pandemonium Avatar asked Jan 07 '23 03:01

Pandemonium


1 Answers

With "truly" anonymous struct return value

If you want to use an anonymous struct return value, that's gonna look really ugly.

Why? Because when you define the return type, you have to describe the anonymous struct. And when you write a return statement, you have to provide the return value which will be a struct literal. A struct literal for an anonymous struct also has to describe the struct!

When you attempt to write this:

func (p *Project) OmitUsername() *struct {
    // return somethig
}

This syntax is not what you think: it doesn't contain the struct definition. Basically in your example the first { is the opening bracket of the anonymous struct definition, and not the opening bracket of the function body. And as such, the subsequent return is interpreted as being inside the anonymous struct definition which is invalid syntax, this is exactly what the error message states too ("syntax error: unexpected return").

It should look like this:

func (p *Project) OmitUsername() *struct {
    Id     uint
    Alias  string
    Data   *json.RawMessage
    Scheme Scheme
} {
    // And now here comes the return statement
}

And if you also add the return statement which has to repeat the anonymous struct definition:

func (p *Project) OmitUsername() *struct {
    Id     uint
    Alias  string
    Data   *json.RawMessage
    Scheme Scheme
} {
    return &struct {
        Id     uint
        Alias  string
        Data   *json.RawMessage
        Scheme Scheme
    }{p.Id, p.Alias, p.Data, p.Scheme}
}

Yes, it's ugly. You can make it a little simpler by using named return value, and not returning a pointer, because zero value of pointers is nil, and to return something, you'd have to initialize it which would also involve repeating the anonymous struct! If you use a non-pointer, named return value, you will have a value of the anonymous struct right away, and you don't have to repeat the anonymous struct definition again, just assign values to its fields:

func (p *Project) OmitUsername2() (ret struct {
    Id     uint
    Alias  string
    Data   *json.RawMessage
    Scheme Scheme
}) {
    ret.Id = p.Id
    ret.Alias = p.Alias
    ret.Data = p.Data
    ret.Scheme = p.Scheme
    return
}

Using them:

p := Project{"Bob", 1, "bobie", nil, nil}
fmt.Println(p.OmitUsername())
fmt.Println(p.OmitUsername2())

Output (try these on the Go Playground):

&{1 bobie <nil> <nil>}
{1 bobie <nil> <nil>}

Still ugly...

With another named type, using embedding

...Best would be to provide another named type to return and not an anonymous struct. You may utilize embedding to make this solution practical and short:

type BaseProject struct {
    Id     uint
    Alias  string
    Data   *json.RawMessage
    Scheme Scheme
}

type Project struct {
    BaseProject
    Username string
}

func (p *Project) OmitUsername() BaseProject {
    return p.BaseProject
}

Using it:

p := Project{BaseProject{1, "bobie", nil, nil}, "Bob"}
fmt.Println(p.OmitUsername())

Output (try this on the Go Playground):

{1 bobie <nil> <nil>}

Note:

Embedding is not really necessary, but this way the fields of the embedded type (BaseProject) will be promoted and so you can refer to them like p.Id as if they were defined in Project. Defining it as a regular field would also work.

like image 90
icza Avatar answered Jan 15 '23 08:01

icza