Thread overview
Killing the array you're iterating over
Feb 25, 2005
Nick Sabalausky
Feb 25, 2005
Nick Sabalausky
Feb 26, 2005
Matthew
Feb 27, 2005
Walter
Feb 28, 2005
Matthew
Mar 16, 2005
David Medlock
February 25, 2005
How safe is it to do this?

MyClass[] array;
... // Put stuff into array
foreach(MyClass obj ; array)
{
   if(/+ some condition that may
         occur in one of the iterations +/)
   {
       array = null;  // Is this kosher?
   }

   ... // Do stuff, but nothing that touches array or obj
}

It works for me and simply has the effect of breaking out of the foreach loop at the end of the iteration. Is this behavior guaranteed? Or it is considered unsafe and likely to be either broken in certain circumstances or disallowed in the future?
February 25, 2005
I forgot to mention that in my particular case the line 'array=null;' is happening from within a member function of obj.  Specifically:

class MyClass
{
  void Foo()
  {
     Kill();
  }
}

MyClass[] array;

void Kill()
{
    array = null;
}

void Run()
{
  ... // Put stuff into array
  foreach(MyClass obj ; array)
  {
     if(/+ some condition that may
         occur in one of the iterations +/)
     {
         obj.Foo();
     }

     ... // Do stuff, but nothing that touches array or obj
  }
}

Nick Sabalausky wrote:
> How safe is it to do this?
> 
> MyClass[] array;
> ... // Put stuff into array
> foreach(MyClass obj ; array)
> {
>    if(/+ some condition that may
>          occur in one of the iterations +/)
>    {
>        array = null;  // Is this kosher?
>    }
> 
>    ... // Do stuff, but nothing that touches array or obj
> }
> 
> It works for me and simply has the effect of breaking out of the foreach loop at the end of the iteration. Is this behavior guaranteed? Or it is considered unsafe and likely to be either broken in certain circumstances or disallowed in the future?
February 26, 2005
I'll be very surprised if this is supported behaviour

"Nick Sabalausky" <a@a.a> wrote in message news:cvobgr$1q8u$1@digitaldaemon.com...
>I forgot to mention that in my particular case the line 'array=null;' is happening from within a member function of obj.  Specifically:
>
> class MyClass
> {
>   void Foo()
>   {
>      Kill();
>   }
> }
>
> MyClass[] array;
>
> void Kill()
> {
>     array = null;
> }
>
> void Run()
> {
>   ... // Put stuff into array
>   foreach(MyClass obj ; array)
>   {
>      if(/+ some condition that may
>          occur in one of the iterations +/)
>      {
>          obj.Foo();
>      }
>
>      ... // Do stuff, but nothing that touches array or obj
>   }
> }
>
> Nick Sabalausky wrote:
>> How safe is it to do this?
>>
>> MyClass[] array;
>> ... // Put stuff into array
>> foreach(MyClass obj ; array)
>> {
>>    if(/+ some condition that may
>>          occur in one of the iterations +/)
>>    {
>>        array = null;  // Is this kosher?
>>    }
>>
>>    ... // Do stuff, but nothing that touches array or obj
>> }
>>
>> It works for me and simply has the effect of breaking out of the foreach loop at the end of the iteration. Is this behavior guaranteed? Or it is considered unsafe and likely to be either broken in certain circumstances or disallowed in the future?


February 27, 2005
In general, it is a very bad idea to try to modify a collection in the middle of a foreach.


February 28, 2005
But is this non-standard behaviour?

I think it should be defined as such for all cases, even if there are some specific ones where it doesn't hold.

Specifically, I think one should always be allowed to modify a collection (assuming it's modifiable) from within a foreach loop, but one the condition that the loop is terminated (by break / return / goto / exception) without any further iterations.

"Walter" <newshound@digitalmars.com> wrote in message news:cvtcmq$qkc$1@digitaldaemon.com...
> In general, it is a very bad idea to try to modify a collection in the middle of a foreach.
>
> 


March 01, 2005
Walter wrote:
> In general, it is a very bad idea to try to modify a collection in the
> middle of a foreach.
> 
> 

In my experience, foreach uses the original keys to iterate over:

char [] [int] collection;
void foo (int key) { ... might add elements to collection ... }
void bar ()
{
	collection[4]="carlos";
	foreach(int key, char [] value; collection)
		foo(key);
}

This will only iterate once.

Since I needed to iterate even with the new elements, I ended up doing something like this:

void bar ()
{
	int [] keys;
	void [int] already;
	while(true)
	{
		keys=collection.keys;
		foreach(int key, char [] value; collection)
			if ( ! (key in already) )
			{
				foo(key);
				already[key];
			}
		if (keys==collection.keys)
			break;
	}
}

I think the algorithm can be improved, but the other solution I tried (keeping another collection with just the items not iterated yet, and adding and removing elements every time), didn't work.

_______________________
Carlos Santander Bernal
March 16, 2005
I agree; it is a common thing to do something such as this:

foreach (inout char c; str)
	if (c == 0)
	{
		// String contains invalid characters.
		str = null;
		break;
	}

Kinda dumb example, but the point is there, I think.

-[Unknown]


> But is this non-standard behaviour?
> 
> I think it should be defined as such for all cases, even if there are some specific ones where it doesn't hold.
> 
> Specifically, I think one should always be allowed to modify a collection (assuming it's modifiable) from within a foreach loop, but one the condition that the loop is terminated (by break / return / goto / exception) without any further iterations.
March 16, 2005
Walter wrote:

> In general, it is a very bad idea to try to modify a collection in the
> middle of a foreach.
> 
> 
Then why are these delegates using inout parameters?

IMO there should be a delegate with an IN parameter or an

void opApply( int delegate( in Type OldValue, out Type NewValue )

type method.

I have already used temp variables in my opApply methods to avoid accidentally doing just this sort of mutation.

-David