Go文本/模板中的niladic函数出现“捕获”错误,并继续执行

If I have a struct with a function like this:

type data struct{}

func (d *data) Foo() (string, error) {
    return "", errors.New("bad")
}

And I call .Foo in a template, like this:

{{ .Foo }}

I get an error:

error calling Foo: bad

This is in line with the documentation for text/template:

  • 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(). Such a method must have one return value (of any type) or two return values, the second of which is an error. If it has two and the returned error is non-nil, execution terminates and an error is returned to the caller as the value of Execute.

Can I define a function to "catch" that error and return some default message, instead of halting execution? For example:

func Catch(val string, err error) string {
    if err != nil {
        return "[render error]"
    } else {
        return val
    }
}

Then:

map := template.FuncMap{"catch": Catch}
tpl := template.Must(template.New("t").Funcs(map).Parse(`
    {{ .Foo | catch }}
`))
b := new(bytes.Buffer)
err := tpl.Execute(b, &data{})

That currently renders an error - is there a way to get it to work?

Here's my very hacky answer, which Calls a method on the internal struct, and then returns "redacted" if you get back a specific error.

// Call calls the given method name on a Message. If the result is
// a PermissionDenied error, return the string "[redacted]" and no error.
//
// This should be used by templates to redact methods. It would be nicer to
// just call the Message directly, but returning an error from a niladic method
// immediately halts template execution, so we need this wrapper around the
// function behavior.
func (r *RedactedMessage) Call(mname string) (interface{}, error) {
    if mname == "" {
        return nil, errors.New("Call() with empty string")
    }
    for _, char := range mname {
        if !unicode.IsUpper(char) {
            return nil, errors.New("Cannot call private method")
        }
        // only check first character
        break
    }
    t := reflect.ValueOf(r.mv)
    m := t.MethodByName(mname)
    if !m.IsValid() {
        return nil, fmt.Errorf("Invalid method: %s", mname)
    }
    vals := m.Call([]reflect.Value{})
    if len(vals) != 2 {
        return nil, fmt.Errorf("Expected to get two values back, got %d", len(vals))
    }
    if vals[1].IsNil() {
        return vals[0].Interface(), nil
    }
    if reflect.DeepEqual(vals[1].Interface(), config.PermissionDenied) {
        return "redacted", nil
    }
    return nil, vals[1].Interface().(error)
}