Live data from Hacker News

How Go detects struct copies with sync.noCopy

func25.dev

51–60 of 82 posts

Re: How Go detects struct copies with sync.noCopy

#51

Earlier quoted context omitted.

I don't know the answer to those things, it feels like it's sort of on the one saying "This is simple" to explain why.

Because it doesn't expand the language and force the user to learn new features, it instead puts the ugliness deep in the library where nobody needs to interact with it.

This explicitly leverages a hidden "feature" of the language, which is how Sync impacts copying (rather unintuively). It leverages this to create an interface based on that hidden feature to create a marker that is consumed by a specific tool (not the compiler).

This feels like at least two layers of special behaviors.

Re: How Go detects struct copies with sync.noCopy

#52
post #25
post #22

Earlier quoted context omitted.

It’s not simple though. The language is simpler, sure. But you pay for language simplicity with program complexity. In go, you have to write and debug a lot more code. I don’t mind spending a few extra weeks learning a more complex language if doing so saves me months of time down the track programming and debugging. That is an excellent investment.

I've not seen this in my experience tbh, the extra code that Go requires is ugly but not complex, the lack of ergonomics actively discourages "clever" solutions and, as a result of this, people tend to write the kind of straightforward code that doesn't end up needing lengthy programming or intense debugging. At my workplace we've used many languages over the years (C#, Python, Go) and Go teams are the ones that by f…

I’ve always felt like the lack of many complex language features nudges teams to also keep their code simple.

There is always two aspects to a language works in practice: how it formally works, and how the community uses it.

Go is (was?) simple, and also (encouraged by the language and influence from its developers) the community mostly aims to keep the usage simple.

Re: How Go detects struct copies with sync.noCopy

#53
post #45

Earlier quoted context omitted.

Rust can handle that with RAII without panicking just fine. And not only Rust..

Sure, you can just ignore errors entirely, and this is what Rust actually does today, at least for std::fs::File. The only other option within RAII that I can think of is that you can mutate some external, longer-lived state. The primary way to deal with error-on-clean-up in RAII languages is to not rely exclusively on RAII for it. Rust's File type, for example, has sync_data and sync_all methods (which, to be fair,…

Rust uses affine types, which means that the compiler guarantees that you clean resources (call the destructor) either zero, or one time. If you call it zero times, then the compiler inserts the call to the destructor for you, in which case there is no opportunity to handle errors in the cleanup, so the result is they are ignored (or you get some kind of panic)

A system that is based on linear types would have an advantage here. In a linear type system, the compiler guarantees that you always call the cleanup function (destructor) exactly once. With such a system, you can have the destructor return an error result, and since the call will always be explicitly written in the code (rather than generated automatically by the compiler), there will always be an explicit errorn handling code branch.

Re: How Go detects struct copies with sync.noCopy

#54
post #22

Earlier quoted context omitted.

Go's full of hacks, and holds no shame over it. Zero-initialized everything, and proceeding to 'defer' instead of RAII, generic builtin types despite lack of generics (until recently), no builtin list type, slices having capacity... This was Go's design philosophy until Rob Pike left - to do the simple thing simply and not try to be clever about it.

It’s not simple though. The language is simpler, sure. But you pay for language simplicity with program complexity. In go, you have to write and debug a lot more code. I don’t mind spending a few extra weeks learning a more complex language if doing so saves me months of time down the track programming and debugging. That is an excellent investment.

[dead]

Re: How Go detects struct copies with sync.noCopy

#55

Earlier quoted context omitted.

Because it doesn't expand the language and force the user to learn new features, it instead puts the ugliness deep in the library where nobody needs to interact with it.

This explicitly leverages a hidden "feature" of the language, which is how Sync impacts copying (rather unintuively). It leverages this to create an interface based on that hidden feature to create a marker that is consumed by a specific tool (not the compiler). This feels like at least two layers of special behaviors.

What's "this", and what does the user need to learn to make use of it?

Re: How Go detects struct copies with sync.noCopy

#56

Earlier quoted context omitted.

This explicitly leverages a hidden "feature" of the language, which is how Sync impacts copying (rather unintuively). It leverages this to create an interface based on that hidden feature to create a marker that is consumed by a specific tool (not the compiler). This feels like at least two layers of special behaviors.

What's "this", and what does the user need to learn to make use of it?

noCopy. In order to understand it, they have to learn that this is enforced only by `go vet` and that the marker relies on everything described in the `2. How go vet and noCopy work` section of this article.

Of course, they don't have to learn anything other than "use `go vet`, it's what enforces this" to use it. But that's true of almost anything?

Re: How Go detects struct copies with sync.noCopy

#57
post #26

Earlier quoted context omitted.

Go's full of hacks, and holds no shame over it. Zero-initialized everything, and proceeding to 'defer' instead of RAII, generic builtin types despite lack of generics (until recently), no builtin list type, slices having capacity... This was Go's design philosophy until Rob Pike left - to do the simple thing simply and not try to be clever about it.

RAII has the advantage that you can't forget to do it, but defer has the advantage that you can handle failure in ways other than panicking. Of course, in many cases (e.g. closing a file), there's generally not much you can do anyway even if you want to handle the error directly, but at least it's possible.

> (e.g. closing a file), there's generally not much you can do anyway

If closing a file fails then you treat it the same as how you would treat a write failure:

  int err = 1;
  FILE *f = fopen("whatever.txt", "w");
  if(f){
    if(5 == fwrite("Hello", 1, 5, f))
      err = 0;

    if(0 != fclose(f))
      err = 1;
  }
  return err;
Code which writes to files and doesn't check for errors on close is subtly incorrect, although my understanding is that kernel devs bend over backwards to make failure unlikely, probably because everybody does it incorrectly anyway.

Re: How Go detects struct copies with sync.noCopy

#58

Earlier quoted context omitted.

It's not really a hack. It's a hint to a static analyser, that's all.

Most languages would encode such behavior in a trait or a protocol instead of a zero-length struct field. It's type information and ought to be encoded in the type. From that perspective, I think it is a hack.

Soooo like a PhantomData in Rust?

Re: How Go detects struct copies with sync.noCopy

#59

Earlier quoted context omitted.

I'd want to blame Go's compatibility guarantee for this, but I can't because it wouldn't actually stop them from adding a proper solution for this.. Just like the magic comments, it's a sad thing to see appear in this language because it feels like magic incantations one has to know that bend over backwards to not actually extend the language to fit the use case.

This shipped with Go 1.7, almost ten years ago to the day.

I don't think this changes anything about the point I was making.

Re: How Go detects struct copies with sync.noCopy

#60
post #7

Earlier quoted context omitted.

Most languages would encode such behavior in a trait or a protocol instead of a zero-length struct field. It's type information and ought to be encoded in the type. From that perspective, I think it is a hack.

This feels like a style complaint and not one about substance. An inaccessible (because it's named _) zero-length struct field is just another kind of metadata. It also doesn't require you to pollute the method set, which would be a bigger issue. The real hack to me is that anything which simply has Lock() and Unlock() methods is considered uncopyable.

> The real hack to me is that anything which simply has Lock() and Unlock() methods is considered uncopyable.

It's a shame they made them generic, taking no arguments and returning nothing; if they'd gone for `Lock(sync.LockType)` or something like that, it would be more difficult to accidentally trigger the `noCopy` flag.

Post reply on HN