英文:
Unexpected return of anonymous struct
问题
我正在尝试实现一个基于原始结构体返回修改后结构体的方法,例如:
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
}
}
我得到以下错误:
models/project.go:22: 语法错误:意外的 return
models/project.go:24: 非声明语句在函数体外
models/project.go:25: 非声明语句在函数体外
models/project.go:25: 语法错误:意外的字符串字面量,期望分号或换行符
models/project.go:26: 非声明语句在函数体外
希望能对你有所帮助。
英文:
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.
答案1
得分: 7
使用“真正”匿名结构体返回值
如果你想使用匿名结构体作为返回值,那么代码看起来会非常丑陋。
为什么呢?因为当你定义返回类型时,你必须描述匿名结构体。而当你编写一个return
语句时,你必须提供将作为结构体字面值的返回值。对于匿名结构体的结构体字面值也必须描述结构体!
当你尝试编写以下代码时:
func (p *Project) OmitUsername() *struct {
// 返回一些东西
}
这个语法不是你想的那样:它并不包含结构体定义。在你的例子中,第一个{
是匿名结构体定义的左大括号,而不是函数体的左大括号。因此,后续的return
被解释为在匿名结构体定义内部,这是无效的语法,这正是错误消息所指出的("syntax error: unexpected return"
)。
应该像这样编写:
func (p *Project) OmitUsername() *struct {
Id uint
Alias string
Data *json.RawMessage
Scheme Scheme
} {
// 然后在这里写返回语句
}
如果你还要添加返回语句,它必须重复匿名结构体的定义:
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}
}
是的,这很丑陋。你可以通过使用具名返回值并且不返回指针来简化它,因为指针的零值是nil
,要返回一个值,你必须初始化它,这也涉及到重复匿名结构体!如果你使用非指针的具名返回值,你将立即拥有匿名结构体的值,而且不需要再次重复匿名结构体的定义,只需为其字段赋值:
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
}
使用它们:
p := Project{"Bob", 1, "bobie", nil, nil}
fmt.Println(p.OmitUsername())
fmt.Println(p.OmitUsername2())
输出结果(在Go Playground上尝试):
&{1 bobie <nil> <nil>}
{1 bobie <nil> <nil>}
仍然很丑陋...
使用另一个具名类型,使用嵌入
...最好的方法是提供另一个具名类型作为返回值,而不是匿名结构体。你可以利用_嵌入_来使这个解决方案实用和简洁:
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
}
使用它:
p := Project{BaseProject{1, "bobie", nil, nil}, "Bob"}
fmt.Println(p.OmitUsername())
输出结果(在Go Playground上尝试):
{1 bobie <nil> <nil>}
注意:
嵌入并不是必需的,但这样做可以提升嵌入类型(BaseProject
)的字段,因此你可以像p.Id
一样引用它们,就像它们在Project
中定义一样。将其定义为常规字段也可以工作。
英文:
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.
答案2
得分: 3
《Go编程语言规范》
关键字
以下关键字是保留的,不能用作标识符。
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
.
func (p *Project) OmitUsername() *struct {
}
struct
是一个保留关键字。
根据您提供的信息不够详细,很难知道您想要什么,也许您想要类似以下的代码?
package main
import (
"encoding/json"
)
type Scheme struct{}
type Project struct {
Id uint
Alias string
Data *json.RawMessage
Scheme Scheme
}
type UserProject struct {
Username string
Project
}
func (u *UserProject) GetProject() *Project {
return &u.Project
}
func main() {}
英文:
The Go Programming Language Specification
The following keywords are reserved and may not be used as identifiers.
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
.
func (p *Project) OmitUsername() *struct {
}
struct
is a reserved keyword.
Without more information about what you are trying to do, it's hard to know what you want, pehaps something like this?
package main
import (
"encoding/json"
)
type Scheme struct{}
type Project struct {
Id uint
Alias string
Data *json.RawMessage
Scheme Scheme
}
type UserProject struct {
Username string
Project
}
func (u *UserProject) GetProject() *Project {
return &u.Project
}
func main() {}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论