-
Notifications
You must be signed in to change notification settings - Fork 18k
proposal: text/template: safe navigation with ?. #43608
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
@cagedmantis @robphoenix @mvdan please any update on this task? It would be super helpful if we can use this style of property resolver. |
@bilak @cagedmantis @robphoenix @mvdan do you have some opinions on this? |
It was 5 months ago this was written, and I find it common courtesy to give a short comment that it has been seen. But each repo to his own. |
This comment was marked as duplicate.
This comment was marked as duplicate.
The way to make progress on this is to turn it into a proposal with a precise definition of the suggested change and the new semantics. For example, if I pass var s struct {
p *struct {
f int
}
} to a template and write The initial comment on the issue says "feel free to add more precise info on how this translates to what needs to be done in text/template" but that is hard for us to do. There are many many many more people suggesting changes than there are people implementing those changes. This can only scale if the people interested in the change can describe exactly what how it should work. Thanks. |
@ianlancetaylor I do not have the knowledge in Go to give specifics, but regarding semantics one can look at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining which can serve as the spec for how it should work. But regarding Anyone more comfortable in Go; feel free to write up a proper proposal. I can link it in the original issue. |
There is no value in nil pointer panics and maybe not much code that depends on it to work properly. Because it just breaks things and doesn't fix anything, the nicest solution would be to make optional chaining the default. Now I know we can't have nice things because of the remote chance that some code depends on the panics, backward compatibility and all, so the next best thing would be to make it a configuration option on template level. |
It seems like this proposal has got a little stuck because those who are proposing it don't have enough familiarity with the template language implementation to state concretely what they would like it to do, but those who do have that knowledge are busy with other things and so unable to try to "meet the proposers in the middle" with some context to work on. I'm admittedly not incredibly invested in this proposal since I don't really use Go's template language in any software I work on, but I do know a little about how it works and so I'm sharing the following in the hope that it gives others some hooks to pull on to make this proposal more concrete. An important starting piece of context for the template language is that all values that the template interacts with are represented as This proposal seems to be focused on the specific detail of what the template language documentation refers to as a "field invocation":
I would recommend that those iterating on this proposal also consider how the new proposed behavior might change or take inspiration from the similar syntax for accessing "a key of the data" (from a map), since that also uses the
The code that handles this kind of traversal today -- including the rule of returning an error if you try to traverse through a nil pointer -- is here: Lines 679 to 761 in f4e3ec3
In particular I think y'all are interested in this particular case: Lines 746 to 758 in f4e3ec3
The key question that this proposal needs to answer, I think, is what replaces the When considering the answer to that, it's important to recognize that the identifier What we do have is the concept of the "zero value" of each type. For a number of different types, including pointers, the zero value is considered to be equal to However, the motivation for this issue linked to a downstream Helm issue involving its That's all the digging and writing I have time for right now. I hope the above is a useful starting point for someone who wants to develop this from a feature request into a proposal. Footnotes
|
Thank you apparentlymart. That is a great summary. You do tell:
Do understand that from my perspective (which hopefully overlaps with the other ones here) is that it's never a problem to have a Ideally, we then either end the sequence with a default value we want to use if none is set or we want it to fail if the value isn't set. That's how people are bringing up the As a side-thought: I read in this sequence of messages that maybe the behavior described of I don't see the need to have a zero for this purpose. If the value doesn't exist, it doesn't exist, it's Examples of how the behavior is with this proposal: I hope I complemented apparentlymart's comment with my own perspective. |
Having |
In my perspective, you always get Am I making sense? |
You are making sense, I'm just saying that you are trading one kind of crash for a different kind of crash. If the code is going to crash anyhow, what's the point of adding the |
I want to reinforce my point that func main() {
v := nil // ERROR: use of untyped nil in assignment
fmt.Println("It's ", v)
} The above fails because
Therefore it isn't really meaningful to say that the template expression result "is I didn't mention in my first comment that the template system does also include the idea of "no value", which is what access to a non-existing map key returns by default. Internally within the template engine, that's represented as the zero value of I see in the Lines 683 to 688 in f4e3ec3
...and so I suppose another possible answer is to say that Between the current behavior and the two possibilities I've discussed so far I've effectively covered all of the existing available options for how to handle accessing a map key that doesn't exist in the map:
If it's possible to do so, I expect that matching one of these already-available behaviors for accessing non-existent map keys as closely as possible is probably best so that the overall template language stays somewhat consistent. (@ungerik also noted that there could be a new option for specifying how the template engine ought to treat field access through a nil pointer, similar to the existing option for missing map keys. That is true, but seems like it would be a materially different proposal since it would make this a global decision for the Go code that renders the template rather than a local decision made by the template author for just one field access.) Footnotes
|
@ianlancetaylor
E.g.2:
Does this help? |
@apparentlymart I was thinking about |
Since I've only been working in theory so far, and that's dangerous 😀, I think probably a good next step would be to confirm whether this is viable by making a temporary edit to the function I've been linking to above so that it uses Although that behavior would not exactly match the proposal -- it would redefine the existing If you want to experiment with it without temporarily modifying |
Yes. Good idea. I don't have a setup on my system to build go but if no one steps forward, I can shift some things around to try finding the time to do that (hopefully, I won't forget about it drowned in my work). |
It would be beautiful if the package
text/template
(used by e.g. Helm) could support optional chaining. Similar to JavaScript in latest standard ECMAScript 2020. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chainingIt would basically look like this:
If service is not defined, it would short-circuit to undefined/false and thus default to "ClusterIP".
This could be used in other cases, such as:
It solves a few things in e.g. Helm that uses
text/template
for templating:default
not working when parent values does not exist in a chain, giving errors, see nil pointer evaluating interface when upper level doesn't exist prevents usage of default function helm/helm#8026.text/template
usages as well), see https://helm.sh/docs/chart_best_practices/values/#flat-or-nested-values. Optional chaining would instead still make the code more readable (one-liner) not caring about value depth. It would also in my opinion make it easier to read YAML files with the nested structure, clearly separating levels, than to use camelCase.As I know very little about Go and the packge
text/template
, feel free to add more precise info on how this translates to what needs to be done intext/template
.The text was updated successfully, but these errors were encountered: