Live data from Hacker News

Go structs are copied on assignment (and other things about Go I'd missed)

jvns.ca

61–70 of 176 posts

Re: Go structs are copied on assignment (and other things about Go I'd missed)

#61
post #28

This sometimes catches out people C#/.Net too, it's a big difference between Class and Struct, Class is reference type and Struct is value type. (see fiddle below), but in practice people very rarely reach for structs, so people don't tend to build up the muscle memory of using them, even if they intuitively understand the difference between reference types and value types from general use of other types. (Fiddle dem…

Non-C# developer question: what use-case/situation would a `struct` make sense to use instead of a `class`? Just out of curiosity. [Edit] Well, there's a nice, special article for this very question: https://learn.microsoft.com/en-us/dotnet/standard/design-gui...

In principle, the answer should be (if we ignored the language community and the model of the stdlib, which we shouldn't do), that structs should be used for most things, and classes only when they are needed. There's nothing a class can do that a struct can't, and structs are not automatically allocated in the heap, so they take some pressure off the GC. Go showed that you can have a fully managed GC language where all types are value types, and get pretty good ergonomics. Java is adding support for value types specifically for performance reasons, and so may have a similar attitude in the far future.

Note that Go does have one feature that makes value types more ergonomic - the ability to explicitly take a reference to one and use it as any other variable, using the syntax of C pointers (but without pointer arithmetic). In C#, if you need a reference to a structs type, your options are more limited. There is `ref` for function parameters, but if you want to store it in a field, you need to use some class type as a box, or perhaps an array of 1 element, since there are no "ref fields" in this sense (there are ref fields, but they are a completely different thing, related to the "ref structs").

Now, in working with real C# code and real C# programmers, all this is false. The community has always preferred using structs almost exclusively for small immutable values, like Color. The standard library and most C# code is not designed with wide use of structs in mind, so it's possible various methods will copy structs unnecessarily around. People aren't used to the semantics of structs, and there is no syntactic difference between structs and classes, so mutable structs will often cause confusion where people won't realize they're modifying a temporary copy instead of the modifying the original.

Re: Go structs are copied on assignment (and other things about Go I'd missed)

#62
post #38
post #15

Earlier quoted context omitted.

That 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…

Is copying huge blocks of data free in 2024? My benchmarks suggest otherwise, and the world still needs assembly programmers.

The 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?

Re: Go structs are copied on assignment (and other things about Go I'd missed)

#63
post #47

Earlier quoted context omitted.

So what should we call "foo(x)" and "foo(ref x)" in C# to distinguish them if not pass-by-value and pass-by-reference?

C# 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.

C#'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)`.

Re: Go structs are copied on assignment (and other things about Go I'd missed)

#64
post #51
post #43

Earlier quoted context omitted.

Python 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 poi…

uh 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)

jerf is correct. Please read this:

https://stackoverflow.com/questions/373419/whats-the-differe...

Re: Go structs are copied on assignment (and other things about Go I'd missed)

#65
post #51
post #43

Earlier quoted context omitted.

Python 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 poi…

uh 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)

> 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.

Re: Go structs are copied on assignment (and other things about Go I'd missed)

#66
post #53

The semantics of when stuff is copied, moved, or passed by reference are all over the place in language design. C started with the idea that functions returned one int-sized value in a register. This led to classic bugs where the function returns a pointer to a local value. Compilers now usually catch this. C eventually got structure return by copy. Then C++ added return value by move, and automatic optimization for…

> This led to classic bugs where the function returns a pointer to a local value. Compilers now usually catch this. C eventually got structure return by copy. Then C++ added return value by move, and automatic optimization for that. Jesus Christ. Can we now get “return fruit by vegetable?” Some speedups may not be worth having to memorize standards documents.

programmers who get the job done without needing to memorise standards or dig into hardware specifics are surely happy enough with general optimisations applied by the compiler like tail call optimisation or copy elision ...

Re: Go structs are copied on assignment (and other things about Go I'd missed)

#67

Earlier quoted context omitted.

No, 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 stru…

You 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

No, 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 x {{1, 2}};
  foo(x);
  std::print("{%d:%d}", x.begin()->first, x.begin()->second);
  //prints {3:4}

  void foo(std::map& a) {
    a = std::map {{3, 4}}; 
  } 
[0] https://go.dev/play/p/6a6Mz9KdFUh

[1] https://onlinegdb.com/j0U2NYbjL

Re: Go structs are copied on assignment (and other things about Go I'd missed)

#68
post #18

Earlier quoted context omitted.

It’s how structs work in C though, and Go is spiritually very close to C, including explicit pointer types, address-taking and dereferencing.

Not necessarily. A string in C is usually a char . If you have a struct with a char and you copy it, you copy the pointer to the backing memory. This is analogous to Go which also has a String be a pointer to the heap, but the behavior is different. Go’s String is a char* that behaves like a char[] when copied.

I think the two *s merged into an italic there

Re: Go structs are copied on assignment (and other things about Go I'd missed)

#69
post #28

This sometimes catches out people C#/.Net too, it's a big difference between Class and Struct, Class is reference type and Struct is value type. (see fiddle below), but in practice people very rarely reach for structs, so people don't tend to build up the muscle memory of using them, even if they intuitively understand the difference between reference types and value types from general use of other types. (Fiddle dem…

This isn't something that unsual though, from some oldies to newer ones, Delphi, Oberon variants, Modula-3, Common Lisp, Eiffel, D, Swift, and eventually Java (when value class and value record finally lands, EA available)

It is the focus on managed scripting languages, that trips people up, when they finally change to one of those compiled ones.

Re: Go structs are copied on assignment (and other things about Go I'd missed)

#70
post #28

This sometimes catches out people C#/.Net too, it's a big difference between Class and Struct, Class is reference type and Struct is value type. (see fiddle below), but in practice people very rarely reach for structs, so people don't tend to build up the muscle memory of using them, even if they intuitively understand the difference between reference types and value types from general use of other types. (Fiddle dem…

[dead]
Post reply on HN