5 ms·
To generalize the title into a rule is good to remember that in Go everything is passed by value(copy).
by zuzuleinen 2y ago
To generalize the title into a rule is good to remember that in Go everything is passed by value(copy).
- jerf 2y agoThat is the case for almost every modern language. C++ is one of the few languages that has "references" and at least last I looked that's a language accommodation over what are pointers being passed by value in the assembly, at least until compiler optimizations take over (and that's not limited to references either). If you're in 2024 and you're in some programming class making a big deal about pass-by-value versus pass-by-reference, ask for your money back and find a course based in this century. Almost literally any topic is a better use of valuable class time than that. From what I've seen of the few unfortunate souls suffering through such a curriculum in recent times is that it literally anti-educates them.
- azundo 2y agoIs python no longer a modern language? Objects are certainly not copied when passed to a function.
- jerf 2y agoPython copies references by value. $ python3 Python 3.12.3 (main, Jul 31 2024, 17:43:48) [GCC 13.2.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> def x(): ... v = 1 ... y(v) ... print(v) ... >>> def y(val): ... val += 1 ... >>> x() 1 A pass-by-reference language would print 2. Everything in a modern language is passed by copy. Exactly what is copied varies and can easily be pointers/references. But there were languages once upon a time that didn't work that way. It's a dead distinction now, though, unless you go dig one of them up. If you want a specific one to look at, look at Forth. Note how when you call a function ("invoke a word", closest equivalent concept), the function/word doesn't get a copy of anything. It directly gets the actual value. There is no new copy, no new memory location, it gets the actual same memory as the caller was using, and not as a "pointer"... directly. Nothing works like that any more.
- froh 2y agouh I'd not say it like that Python passes primitive types by value, out rather "as if by value", because it copies them on write. if you modify your experiment to pass around a dict or list and modify that in the 'y', you'll see y is happily modified. so Python passes by reference, however it either blocks updates (tuple) or copies on write (int, str, float) or updates in place (dict, list, class)
- js2 2y agojerf is correct. Please read this: https://stackoverflow.com/questions/373419/whats-the-difference-between-passing-by-reference-vs-passing-by-value https://stackoverflow.com/questions/373419/whats-the-differe...
- tsimionescu 2y ago> if you modify your experiment to pass around a dict or list and modify that in the 'y', you'll see y is happily modified. No, you won't. x = {'a' : 1} foo(x) print(x) def foo(z): z = {'b' : 2} You'll see that this prints `{'a' : 1}`, not `{'b' : 2}`. Python always uses pass-by-value. It passes a copy of the pointer to a dict/list/etc in this case. Of course, if you modify the fields of the z variable, as in `z['b'] = 2`, you do modify the original object that is referenced by z. But this is not pass-by-reference.
- dwattttt 2y agoIs it not pass-by-reference by some technicality? In the mutation example you suggest, if a reference to x isn't being passed into foo, how could foo modify x? I would sooner believe the example is showing you shadowing the z argument to foo, than foo being able to modify the in-parameter sometimes even if it's pass by value.
- jerf 2y ago"In the mutation example you suggest, if a reference to x isn't being passed into foo, how could foo modify x?" Because it is passing a pointer by value under the hood. This is the part that messes everyone up. Passing pointers by value is not what passing by reference used to mean. And it matters, precisely because that is extremely realistic Python code that absolutely will mess you up if you don't understand exactly what is going on. You were passed a reference by value. If you go under the hood, you will find it is quite literally being copied and a ref count is being incremented. It's a new reference to the same stuff as the passed-in reference. But if you assign directly to the variable holding that reference, that variable will then be holding the new reference. This is base level, "I'd use it on an interview to see if you really know Python", level stuff. Everything in a modern language involves passing things by value. Sometimes the language will gloss over it for you, but it's still a gloss. There were languages where things fundamentally, at the deepest level, were not passed by value. They're gone. Passing references by copy is not the same thing, and that Python code is precisely why it's not the same thing.
- klyrs 2y agoIs copying huge blocks of data free in 2024? My benchmarks suggest otherwise, and the world still needs assembly programmers.
- jerf 2y agoSee cousin posts. That's not what the terms mean.
- tsimionescu 2y agoThe way almost all programming languages work is that they explicitly pass a copy of a pointer to a function. That is, in almost all languages used today, whether GC or not, assigning to a function parameter doesn't modify the original variable in the calling function. Assigning to a field of that parameter will often modify the field of the caller's local variable, though. That is, in code like this: ReferenceType a = {myField: 1} foo(a) print(a.myField) void foo(ReferenceType a) { a.myField = 9 a = null } Whether you translate this pseudocode to Python, Java, C# (with `class RefType`), C (`RefType = *StructType`), Go (same as C), C++ (same as C), Rust, Zig etc - the result is the same: the print will work and it will say 9. The only exceptions where the print would fail with a null pointer issue that I know of are C++'s references and C#' s ref parameters. Are there any others?
- klyrs 2y agoRight. Passing pointers is much cheaper than passing values of large structures. And then references are an abstraction over pointers that allow further compile-time optimization in languages that support it. Pass-by-value, pass-by-pointer, and pass-by-reference are three distinct operational concepts that should be taught to programmers.
- tsimionescu 2y agoI think the right mental model is pass-by-value for the first two. There is nothing different in the calling convention between sending a parameter of type int* vs a parameter of type int. They are both pass-by-value. The value of a pointer happens to be a reference to an object, while the value of an int is an int. In both cases, the semantics is that the value of the expression passed to the function is copied to a local variable in the function. Depending on the language, that is very likely the whole picture of how function calls work. In a rare few modern languages, this is not true: in C# and C++, when you have a reference parameter, things get sonewhat more complicated. When you pass an expression to a reference parameter, instead of copying the value of evaluating that expression into the parameter of the function, the parameter is that value itself. It's probably easier to explain this as passing a pointer to the result of the expression + some extra syntax to auto-dereference the pointer.
- metaltyphoon 2y agoBoth C# and Swift makes a distinct difference by having both struct and classes.
- jerf 2y agoThat is not what the pass-by-copy vs. pass-by-reference distinction is. Both are passing by value, one is just a pointer (under the hood) and the other is not. But the incoming parameter is a copy. See my cousin post: https://news.ycombinator.com/item?id=41220384 https://news.ycombinator.com/item?id=41220384 This is a distinction so dead that people basically assume that this must be talking about whether things are passed by pointer, because in modern languages, what else would it be? But that is not what pass-by-reference versus pass-by-copy means. This is part of why it's a good idea to let the terminology just die.
- magicalhippo 2y agoSo what should we call "foo(x)" and "foo(ref x)" in C# to distinguish them if not pass-by-value and pass-by-reference?
- jerf 2y agoC# can call it that specifically if it likes, because the general computer science term is dead, but under the hood you're passing a reference by value. Look to the generated assembler in a non-inlined function. You'll find a copy of a pointer. You did not in true pass-by-refernce langauges. The fact that is a sensible thing to say in a modern language is another sign the terminology is dead.
- tsimionescu 2y agoC#'s ref parameters, same as C++' s reference types, have true pass-by-reference semantics. Whether this gets compiled to pass-by-pointer or not is not observable from the language semantics. That is, the following holds true: int a = 10; foo(ref a) ; Assert(a == 100); void foo(ref int a) { a = 100; } There's also a good chance that in this very simple case that the compiler will inline foo, so that it will not ever pass the address of a even at the assembly level. The same would be true in C++ with `void foo (int& a)`.
- jasomill 2y agoFor a last-century example of actual pass-by-reference, assign-by-copy, the PL/I program foo: proc options(main); dcl sysprint print; dcl a(3) fixed bin(31) init(1,2,3); put skip data (a); call bar(a); put skip data (a); bar: proc(x); dcl x(3) fixed bin(31); dcl b(3) fixed bin(31) init(3,2,1); x = b; b(1) = 42; x(2) = 42; put skip data (b); put skip data (x); end bar; end foo; outputs A(1)= 1 A(2)= 2 A(3)= 3 ; B(1)= 42 B(2)= 2 B(3)= 1 ; X(1)= 3 X(2)= 42 X(3)= 1 ; A(1)= 3 A(2)= 42 A(3)= 1 ; demonstrating that X refers to A in BAR and assigning B to X copies B into X (= A).
- pjmlp 2y agoSure, if they want to be bad developers in C#, F#, Swift, D, Rust, Ada, VB, Delphi, just to stay on the ones that are kind of relevant in 2024 for business, for various level of relevant, versus the ones story has forgotten about.
- telotortium 2y agoRust doesn't have pass-by-reference in the sense that C# or C++ do. The `&` and `&mut` types are called references[1], but what is meant by "reference" is the C++ guarantee that a reference always points to a valid value. This C++ code uses references without explicit pointer deferencing: #include <iostream> #include <string> void s_ref_modify(std::string& s) { s = std::string("Best"); return; } int main() { std::string str = "Test"; s_ref_modify(str); std::cout << str << '\n'; } The equivalent Rust requires passing a mutable pointer to the callee, where it is explicitly dereferenced: fn s_ref_modify(s: &mut String) { *s = String::from("Best"); } fn main() { let mut str = String::from("Test"); s_ref_modify(&mut str); println!("{}", str); } Swift has `inout` parameters, which superficially look similar to pass-by-reference, except that the semantics are actually copy-in copy-out[2]: > In-out parameters are passed as follows: > 1. When the function is called, the value of the argument is copied. > 2. In the body of the function, the copy is modified. > 3. When the function returns, the copy’s value is assigned to the original argument. > This behavior is known as _copy-in copy-out_ or _call by value result_. For example, when a computed property or a property with observers is passed as an in-out parameter, its getter is called as part of the function call and its setter is called as part of the function return. [1]: https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html https://doc.rust-lang.org/book/ch04-02-references-and-borrow... [2]: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/declarations#In-Out-Parameters:~:text=In%2Dout%20parameters%20are%20passed,part%20of%20the%20function%20return https://docs.swift.org/swift-book/documentation/the-swift-pr....
- erik_seaberg 2y agoMaps and channels and functions are passed by reference. Slices are passed and returned by value but sometimes share state invisibly, the worst of both worlds. It would make more sense if Go either made this stuff immutable, made defensive copies, or refused and required using explicit pointers for all these cases.
- dondraper36 2y agoNo, it's not the case and this terminology shouldn't be used as it's confusing and unhelpful. There are reference types in Go even though this is also not a super popular term. They still follow the pass-by-value semantics, it's just that a pointer is copied. A map is effectively a pointer to hmap data structure. In the early days of Go, there was an explicit pointer, but then it was changed. Slices are a 3-word structure internally that includes a pointer to a backing array and this is why it's also a "reference type". That said, everything is still passed by value and there are no references in Go.
- jxndndnjc 2y agoYou are splitting hair, Maps are effectively references. That's like saying C++ doesn't have references since it's just a pointer being copied around
- tsimionescu 2y agoNo, there is a real difference, this is not splitting hairs. Go is always pass-by-value, even for maps [0]: x := map[int]int{1: 2} foo(x) fmt.Printf("%+v", x) //prints map[1:2] func foo(a map[int]int) { a = map[int]int{3: 4} } In contrast, C++ references have different semantics [1]: std::map<int, int> x {{1, 2}}; foo(x); std::print("{%d:%d}", x.begin()->first, x.begin()->second); //prints {3:4} void foo(std::map<int, int>& a) { a = std::map<int, int> {{3, 4}}; } [0] https://go.dev/play/p/6a6Mz9KdFUh https://go.dev/play/p/6a6Mz9KdFUh [1] https://onlinegdb.com/j0U2NYbjL https://onlinegdb.com/j0U2NYbjL
- 2y ago
- Dylan16807 2y agoBut the author already knew that. The important lesson is that assignments are by value(copy).