-
Notifications
You must be signed in to change notification settings - Fork 17.9k
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
bytes: Runtime mutations of variables without pointers and unsafe.* operations in user code. #28554
Comments
Yes, this is how slices work. See https://blog.golang.org/go-slices-usage-and-internals . |
Hi Ian! First of all - thank you for your time! I think it's not so obvious behaviour, 'cause it feels like (and work so) passing slice elements (of an array) by reference, but Dave Cheney (@davecheney) talking to us that Go is a "pass by value language" and "there is no pass-by-reference in Go" (https://dave.cheney.net/2017/04/29/there-is-no-pass-by-reference-in-go). This got stuck in my memory. =) https://blog.golang.org/go-slices-usage-and-internals
So, to prevent this implicit behaviour we must explicitly allocate array in slice via But, to prevent such a "Whoa!" effect for newbie Gophers this implicit behavior of slices must be reflect in documentstion of the Go language. https://golang.org/ref/spec#Slice_types Honestly, for me, with even more than three years of practitioning in Go and build projects with Go, doesn't understand what's wrong and doesn't spot this behaviour from the first sight — "simplicity is not so simple" as a Rob Pike (@robpike) said, but also simplicity is not so obvious! This behaviour reflects in "Effective Go" only, but also needs to be present in language specification. https://golang.org/doc/effective_go.html#slices About an arrays itself:
About slices:
And this is true and applied to the maps also:
https://golang.org/doc/effective_go.html#allocation_new
But, I think, if we need such eficiency, we always can use a pointer to an array, slice or map — nevertheless slices and maps passed by reference for efficiency by default, and can mutate. Very controversial decision by design.
So, I think it could be potentially the root of many misunderstandings and bugs (especially in cryptography work, like in our project), not only for newbies, but also for more experienced Go developers with a background in other languages and polyglot in mind (like me), 'cause when we pass slice into the function argument, or assign a slice into other variable (and here must be possibility of But for now we have a such economy on slice and maps variables and bytes, much mutation, just for efficiency.
Because of such things, Go doesn't follow the "principle of least astonishment" (https://en.wikipedia.org/wiki/Principle_of_least_astonishment). So, thus I think this behaviour of slices must be at least cover by statically checking and compier warning (like "use make() to allocate the slices, little gopher!" =), and also intergrated into the LSP and debugging tools for IDEs, like https://github.com/sourcegraph/go-langserver And guys, please, take a look at the on-line Go playground - there's something really strange happening with the program output of the first example (in contrast to the local terminal output): ============ |
It's far too late to change how slices work in Go today. You say that it should be documented in the language spec, and it is: you are quoting the exact lines where it is documented. I'm sorry you found the behavior confusing, but I'm not sure what we should do here. Thousands of existing Go programs rely on the current semantics. We aren't going to add a compiler warning. It's quite unlikely that we would add a vet warning, even if we could figure out what to warn about. |
Thanks for an answer Ian! ============ |
Ian, please, take a look at this two examples. Here's the earlier two simplified examples (based on production code, in which we observed such unexpected behaviour initially) - this code use file IO, instead of buffers (as in examples above). When we use file IO ( https://play.golang.org/p/G4uMh6pCwOx Otherwise, when we use encryption without file IO and encrypt data in-place or just use in-source hard-coded slice of an array of bytes in hex representation (of encrypted data) - we don't have a mutation of the source encrypted data in https://play.golang.org/p/TnUmiHFy7iC https://play.golang.org/p/3kfHf1NpE2I In both cases we use slices of byte arrays, similar data-types, but have different semantics - mutation of source data and not. Is it correct semantics? And if so - please, can you explain why and how does it happen? 'Cause I feels strong confusing and misunderstanding of this. ============ |
Hi @andrcmdr - I am very sorry, but we only use the issue tracker for bugs and feature proposals. For general questions like these, please feel free to make good use of the forums mentioned in the Questions wiki page. That has much more reach and is more suitable for general discussions like these. Alternatively, if you have a new suggestion or proposal, please do open a new issue. Thank you ! |
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
Variant 1: https://play.golang.org/p/FUSIhyjg2y2
Variant 2 (almost same, another way to initialize readBufferCopy4 variable): https://play.golang.org/p/0aB0m2XMH4D
What did you expect to see?
readBufferCopy1 == readBufferCopy2 == readBufferCopy3 == readBufferCopy4 == readBufferCopy5 == readBuffer == contentToWriteOut (untouched encrypted content)
What did you see instead?
Almost all variables decrypted with addition of InitVector at beginning, excluding readBufferCopy5, readBufferCopy4 in make() case;
=====
Additionaly mentioning @andrcmdr for additional comments by this case and watching.
The text was updated successfully, but these errors were encountered: