www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Gotchas for returning values from blocks

reply jmh530 <john.michael.hall gmail.com> writes:
I was thinking about how Rust can return arbitrarily from blocks. 
It occurred to me recently that there's no reason you can't do 
that in D. I'm just not sure if there are any limitations. For 
instance, in the code below, I create an object but don't 
allocate anything, then in a block I create a garbage collected 
variable and assign it to it. Everything seems to work fine. I'm 
just not sure if there are any gotchas to be aware of.

class Foo
{
	int baz = 2;
}

void main()
{
	import std.stdio : writeln;
	
	Foo foo;
	{
		Foo bar = new Foo();
		foo = bar;
	}
	//bar is now out of scope
	assert(foo.baz == 2);
}
Jun 12 2016
next sibling parent reply Era Scarecrow <rtcvb32 yahoo.com> writes:
On Sunday, 12 June 2016 at 18:24:58 UTC, jmh530 wrote:
 I'm just not sure if there are any gotchas to be aware of.
Aside from forgetting it's it's own block, you might add a return statement to it and leave the entire function. Or forget what's in what scope (assuming you do more than 1-2 pages of code per function). They do sorta behave like inlined void delegate functions (skip the call & heavy stack management). Unless there's a good reason for using blocks I don't see the need, since you will likely use inner scopes in if statements and the like anyways.
Jun 12 2016
parent jmh530 <john.michael.hall gmail.com> writes:
On Sunday, 12 June 2016 at 19:30:49 UTC, Era Scarecrow wrote:
 On Sunday, 12 June 2016 at 18:24:58 UTC, jmh530 wrote:
 I'm just not sure if there are any gotchas to be aware of.
Aside from forgetting it's it's own block, you might add a return statement to it and leave the entire function. Or forget what's in what scope (assuming you do more than 1-2 pages of code per function). They do sorta behave like inlined void delegate functions (skip the call & heavy stack management). Unless there's a good reason for using blocks I don't see the need, since you will likely use inner scopes in if statements and the like anyways.
Good points. Thanks for the reply.
Jun 12 2016
prev sibling parent reply Mike Parker <aldacron gmail.com> writes:
On Sunday, 12 June 2016 at 18:24:58 UTC, jmh530 wrote:

 garbage collected variable and assign it to it. Everything 
 seems to work fine. I'm just not sure if there are any gotchas 
 to be aware of.

 class Foo
 {
 	int baz = 2;
 }

 void main()
 {
 	import std.stdio : writeln;
 	
 	Foo foo;
 	{
 		Foo bar = new Foo();
 		foo = bar;
 	}
 	//bar is now out of scope
 	assert(foo.baz == 2);
 }
Everything works fine in your example because 'new' always allocates on the heap. Anything allocated on the stack is not guaranteed to be valid after the scope exits: struct Foo { int baz; ~this() { baz = 1; } } void main() { import std.stdio : writeln; Foo* foo; { Foo bar = Foo(10); foo = &bar; } //bar is now out of scope assert(foo.baz == 10); } Struct constructors are always run when exiting a scope. More importantly, the pointer to bar is only valid until the stack address where it lives is overwritten by another stack allocation. In this example, there's no chance for that to happen before I access it, but it could happen at any time.
Jun 12 2016
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Monday, 13 June 2016 at 01:41:07 UTC, Mike Parker wrote:
 Everything works fine in your example because 'new' always 
 allocates on the heap. Anything allocated on the stack is not 
 guaranteed to be valid after the scope exits:

 struct Foo
 {
     int baz;
     ~this() { baz = 1; }
 }

 void main()
 {
     import std.stdio : writeln;

     Foo* foo;
     {
         Foo bar = Foo(10);
         foo = &bar;
     }
     //bar is now out of scope
     assert(foo.baz == 10);
 }

 Struct constructors are always run when exiting a scope. More 
 importantly, the pointer to bar is only valid until the stack 
 address where it lives is overwritten by another stack 
 allocation. In this example, there's no chance for that to 
 happen before I access it, but it could happen at any time.
So returning a reference to something on the stack is a bad idea, but copying the value would be fine.
Jun 13 2016
parent Era Scarecrow <rtcvb32 yahoo.com> writes:
On Monday, 13 June 2016 at 14:16:58 UTC, jmh530 wrote:
 So returning a reference to something on the stack is a bad 
 idea, but copying the value would be fine.
This is easy enough to get wrong elsewhere too. I recall having an issue with a foreach, until I added a 'ref' to it. Looking at the addresses all pointing to the same spot (the temporary) which can add curiously subtle bugs, or blatantly obvious ones.
Jun 13 2016