Go模板无法在字段上调用方法
Go template can't call method on field
我有一个围绕 net/mail.Address
的包装器,它提供了一些编组逻辑。我试图在模板中使用它,但我一直收到 can't evaluate field String in type EmailAddress
。模板文档说:
The name of a niladic method of the data, preceded by a period,
such as
.Method
The result is the value of invoking the method with dot as the
receiver, dot.Method().
和
Method invocations may be chained and combined with fields and keys
to any depth:
.Field1.Key1.Method1.Field2.Key2.Method2
考虑到这一点,我写了这篇文章:
package main
import (
"bytes"
"fmt"
"html/template"
"net/mail"
"os"
)
type EmailAddress struct{ mail.Address }
type emailFormatter struct {
From EmailAddress
To EmailAddress
}
var tmpl = template.Must(template.New("Sample Text").Parse("From: {{.From.String}}\r" + `
To: {{.To.String}}` + "\r" + `
Content-Type: text/html` + "\r" + `
Subject: Sample Text` + "\r\n\r" + `
<!DOCTYPE html>
<html lang="en">
<head>
<title>Sample Text</title>
<meta charset="utf-8"/>
</head>
<body>
Sample Text
</body>
</html>
`));
func main() {
to := EmailAddress{
mail.Address{
Address: "em@i.l",
Name: "",
},
}
from := EmailAddress{
mail.Address{
Address: "no-reply@test.quest",
Name: "",
},
}
fmt.Println(to.String()) //outputs (as expected) "<em@i.l>"
fmt.Println(from.String()) //outputs (as expected) "<no-reply@test.quest>"
f := emailFormatter{
To: to,
From: from,
}
var buff bytes.Buffer
if err := tmpl.Execute(&buff, f); err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}
fmt.Println(buff.String())
}
我已经单独验证调用EmailAddress.String
是完全合法的,所以我不明白为什么只有这样的输出:
Error: template: Sample Text:1:13: executing "Sample Text" at <.From.String>: can't evaluate field String in type main.EmailAddress
编辑
根据评论者的建议,我将调用从 .From.String
和 .To.String
更改为 .From.Address.String
和 .To.Address.String
,因为
"String
isn't defined on EmailAddress
, it's defined on EmailAddress.Address
"
但结果是一样的:
Error: template: Sample Text:1:13: executing "Sample Text" at <.From.Address.String>: can't evaluate field String in type mail.Address
由于 String
是使用指针接收器定义的,因此您需要将 mail.Address
的 "addressable" 实例传递给模板才能执行该方法。
您可以通过传递指向 f
的指针来完成此操作。
if err := tmpl.Execute(&buff, &f); err != nil {
panic(err)
}
或者您可以通过传递指向 EmailAddress
.
的指针来实现
type emailFormatter struct {
From *EmailAddress
To *EmailAddress
}
// ...
f := emailFormatter{
To: &to,
From: &from,
}
// ...
if err := tmpl.Execute(&buff, f); err != nil {
panic(err)
}
或者传入指向 mail.Address
的指针。
type EmailAddress struct{ *mail.Address }
// ...
to := EmailAddress{
&mail.Address{
Address: "em@i.l",
Name: "",
},
}
from := EmailAddress{
&mail.Address{
Address: "no-reply@test.quest",
Name: "",
},
}
f := emailFormatter{
To: to,
From: from,
}
// ...
if err := tmpl.Execute(&buff, f); err != nil {
panic(err)
}
请注意,您不需要在 Go 代码中执行此操作的原因是编译器会为您执行此操作。
例如:
fmt.Println(to.String())
变为:
fmt.Println((&to).String())
A method call x.m() is valid if the method set of (the type of) x
contains m and the argument list can be assigned to the parameter list
of m. If x is addressable and &x's method set contains m, x.m() is
shorthand for (&x).m()
我有一个围绕 net/mail.Address
的包装器,它提供了一些编组逻辑。我试图在模板中使用它,但我一直收到 can't evaluate field String in type EmailAddress
。模板文档说:
The name of a niladic method of the data, preceded by a period, such as
.Method
The result is the value of invoking the method with dot as the receiver, dot.Method().
和
Method invocations may be chained and combined with fields and keys to any depth:
.Field1.Key1.Method1.Field2.Key2.Method2
考虑到这一点,我写了这篇文章:
package main
import (
"bytes"
"fmt"
"html/template"
"net/mail"
"os"
)
type EmailAddress struct{ mail.Address }
type emailFormatter struct {
From EmailAddress
To EmailAddress
}
var tmpl = template.Must(template.New("Sample Text").Parse("From: {{.From.String}}\r" + `
To: {{.To.String}}` + "\r" + `
Content-Type: text/html` + "\r" + `
Subject: Sample Text` + "\r\n\r" + `
<!DOCTYPE html>
<html lang="en">
<head>
<title>Sample Text</title>
<meta charset="utf-8"/>
</head>
<body>
Sample Text
</body>
</html>
`));
func main() {
to := EmailAddress{
mail.Address{
Address: "em@i.l",
Name: "",
},
}
from := EmailAddress{
mail.Address{
Address: "no-reply@test.quest",
Name: "",
},
}
fmt.Println(to.String()) //outputs (as expected) "<em@i.l>"
fmt.Println(from.String()) //outputs (as expected) "<no-reply@test.quest>"
f := emailFormatter{
To: to,
From: from,
}
var buff bytes.Buffer
if err := tmpl.Execute(&buff, f); err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}
fmt.Println(buff.String())
}
我已经单独验证调用EmailAddress.String
是完全合法的,所以我不明白为什么只有这样的输出:
Error: template: Sample Text:1:13: executing "Sample Text" at <.From.String>: can't evaluate field String in type main.EmailAddress
编辑
根据评论者的建议,我将调用从 .From.String
和 .To.String
更改为 .From.Address.String
和 .To.Address.String
,因为
"
String
isn't defined onEmailAddress
, it's defined onEmailAddress.Address
"
但结果是一样的:
Error: template: Sample Text:1:13: executing "Sample Text" at <.From.Address.String>: can't evaluate field String in type mail.Address
由于 String
是使用指针接收器定义的,因此您需要将 mail.Address
的 "addressable" 实例传递给模板才能执行该方法。
您可以通过传递指向 f
的指针来完成此操作。
if err := tmpl.Execute(&buff, &f); err != nil {
panic(err)
}
或者您可以通过传递指向 EmailAddress
.
type emailFormatter struct {
From *EmailAddress
To *EmailAddress
}
// ...
f := emailFormatter{
To: &to,
From: &from,
}
// ...
if err := tmpl.Execute(&buff, f); err != nil {
panic(err)
}
或者传入指向 mail.Address
的指针。
type EmailAddress struct{ *mail.Address }
// ...
to := EmailAddress{
&mail.Address{
Address: "em@i.l",
Name: "",
},
}
from := EmailAddress{
&mail.Address{
Address: "no-reply@test.quest",
Name: "",
},
}
f := emailFormatter{
To: to,
From: from,
}
// ...
if err := tmpl.Execute(&buff, f); err != nil {
panic(err)
}
请注意,您不需要在 Go 代码中执行此操作的原因是编译器会为您执行此操作。
例如:
fmt.Println(to.String())
变为:
fmt.Println((&to).String())
A method call x.m() is valid if the method set of (the type of) x contains m and the argument list can be assigned to the parameter list of m. If x is addressable and &x's method set contains m, x.m() is shorthand for (&x).m()