Go: proposal: Go 2: defer/init methods for structs

Created on 2 Sep 2017  ·  18Comments  ·  Source: golang/go

Hello,
I would like to propose defer and init functions for structs that are applied when a struct is created.
Examples:

type MyStruct struct {
id int
}

func (ms *MyStruct) defer(returned boolean) {
//...code
}

func (ms *MyStruct) init(params ....) {
//...code
}

All the compiler would do is:

  1. Detect if struct has init() and defer() function members associated with it.
  2. If Yes, execute them right after struct variable declaration.
  3. Detect whether it's returned, if yes, defer defer(true) else defer defer(false)
    Example:

var myStruct= MyStruct{}
OR
var myStruct= MyStruct{}(params)

OR Even
var myStruct = MyStruct()

Maybe MyStruct{}() Or MyStruct() is forced by compiler when the struct is implementing init() and/or defer() methods while MyStruct{} for when they are not implemented.

Would be done in the compiler as:

var myStruct= MyStruct{}
myStruct.init(params)
defer myStruct.defer(returned)
//provided that myStruct has init() and defer() functions associated with it of course.

Rust has a similar way of doing this also.

OR other forms:

defer func (ms *MyStruct) {
//...code
}
func (ms *MyStruct)() {
//...code
}

OR all just through structs

type MyStruct struct {
init func()=func(){}
id int =0
defer func()=func(){}
}

Thank You,

FrozenDueToAge Go2 LanguageChange Proposal

Most helpful comment

init functions are not needed in Go: idiomatic New functions work nicely, and struct literals are often even better. (See https://golang.org/doc/effective_go.html#composite_literals.)

init functions are often used in C because there is otherwise no way to construct stack-allocated values containing internal pointers, and in the no-exceptions dialect of C++ because there is otherwise no way to surface errors from the constructor. Thankfully, Go does not suffer from either of those problems.

An idiomatic version of your example in Go would look more like:

s := NewMyStruct(params)
defer s.Close()

All 18 comments

I'm not sure what you mean by struct interface, but it sounds like your proposing a finaliser or destructor of some kind.

On 2 Sep 2017, at 09:39, Medo notifications@github.com wrote:

Hello,
I would like to propose defer func.
Example:
When a struct (pointer) is created, any defers that are registered to that struct through its struct interface would be executed.

type MyStruct struct {
id int
}

defer func (ms *MyStruct) {
//...code
}

Thank You,


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or mute the thread.

@davecheney edited.

@medozs thank you for your edit. I'm sorry that I don't know what this proposal is proposing.

Can you please explain what you cannot do today, and how this proposal would allow you to do the thing.

Thanks

@davecheney My proposal is similar constructor/destructor for objects in OOP. Instead of calling the constructor method init() inside our function every time to initialize our structure values, and instead of calling the defer function with the keyword defer inside function, I am proposing that all this is done automatically when strict is declared in function.

When should be the destructor called? Should be running the destructor guaranteed to eventually occur? How's the destructor different wrt the existing finalizers?

@cznic
My proposal is:
Instead of manually doing
myStruct.init()
defer myStruct.defer()
I propose that these methods to be applied automatically as soon as struct object is created.
So just:
var myStruct= MyStruct{} would apply these defer() and init() functions.

When does the defer statement run?

On 3 Sep 2017, at 04:07, Medo notifications@github.com wrote:

@cznic
My proposal is:
Instead of manually doing
myStruct.init()
defer myStruct.defer()
I would these methods to be applied automatically as soon as struct object is created.
So just:
var myStruct= MyStruct{} would apply these defer() and init() functions.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

@davecheney As soon as struct variable is created in a function or through function parameters. defer() function would be deferred, meaning it will be finally executed at the end of the function struct variable is declared in.

What would happen to any references to that stuff which may have escaped, for example via closure, via return value, via being stored in a shared variable, or passed as a parameter to a go statement?

On 3 Sep 2017, at 07:46, Medo notifications@github.com wrote:

@davecheney As soon as struct variable is created in a function or through function parameters. defer() function would be deferred, meaning it will be finally executed at the end of the function struct variable is declared in.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

@davecheney These things are up to programmer to analyze and decide. GC or programmer can take care of things escaping as well. There shouldn't be return values associated with init() or defer() functions. Basically all the compiler should do is:

  1. Detect if struct has init() and defer() function members associated with it.
  2. If Yes, execute them right after struct variable declaration.
    Example:

var myStruct= MyStruct{}

would be done in the compiler as:

var myStruct= MyStruct{}
myStruct.init()
defer myStruct.defer()
//provided that myStruct has init() and defer() functions associated with it of course.

So values of types which have automatic defer and init statements cannot leave the function they are declared in?

What happens if one of those is assigned to a field in a struct and a reference to that struct is returned?

What happens if a defer/init enabled typed is stored in a a map, and the map returned?

On 3 Sep 2017, at 07:59, Medo notifications@github.com wrote:

@davecheney These things are up to programmer to analyze and decide. GC or programmer can take care of things escaping as well. There shouldn't be return values associated with init() or defer() functions.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

@davecheney My proposal is not concerned with these values. It's just concerned with executing init() and defer() functions automatically instead of manually. init() at the beginning of struct var declaration, and deferring defer() function to be executed as the end of function.

init functions are not needed in Go: idiomatic New functions work nicely, and struct literals are often even better. (See https://golang.org/doc/effective_go.html#composite_literals.)

init functions are often used in C because there is otherwise no way to construct stack-allocated values containing internal pointers, and in the no-exceptions dialect of C++ because there is otherwise no way to surface errors from the constructor. Thankfully, Go does not suffer from either of those problems.

An idiomatic version of your example in Go would look more like:

s := NewMyStruct(params)
defer s.Close()

Destructors are generally problematic in garbage-collected languages, since a garbage collector usually doesn't give any guarantees when unreachable objects are freed, or whether they are freed at all. (A program may exit before a particular unreachable object is freed.) It may be something to put in the unsafe package, but I believe it shouldn't be part of the language proper.

C++ is either manually managed or relies on reference counting (via smart pointers), so the case there is very different.

@pcostanza

a garbage collector usually doesn't give any guarantees when unreachable objects are freed

I think you are confusing destructors with finalizers. Go already has finalizers, and they have exactly the problems you mention.

The way I read this proposal, it is about destructors that run at the end of the scope in which a variable is declared, rather than when the underlying value becomes reachable. Those have their own problems (namely, use-after-destruction bugs), but those problems are more closely related to data races and typestate bugs than they are to finalizers.

Note that defer is already prone to the same sorts of bugs as destructors: the main advantages of the explicit defer are that calls can refer to other variables from the surrounding scope, and that the explicit defer statement signals to the reader to pay extra attention to the risk of typestate bugs.

This looks more like IDisposable in C# where it guaranteed that Dispose method will be called at the end of usage block. Extremely useful in C# and doesn't conflict with GC. But does introduce a problem with use-after-destruction, which throws ObjectDisposedException

I have a problem with this feature as I don't see the point of having a defer method on structure types. I mean, this really looks like the C++  std::unique_ptr<T> wrapper type. What are the benefits of this over the builtin GC step that would free the struct when needed ?

Would you not rather have the defer method being called upon Garbage Collection of such a struct ?

This is a partial addition of C++ constructors and destructors to Go. It's not sufficient; there is a reason that C++ also has move constructors and assignment constructors. It's also incomplete: when does the destructor run for values that are on the heap? That is to say, for values allocated using new? In C++ the problem is simpler because there is no garbage collection and because the lifetime of values is controlled explicitly by the program. In Go the lifetime may change if the address is taken or depending on when garbage collection runs.

Proposal declined.

Was this page helpful?
0 / 5 - 0 ratings