Thread overview
Is it bad practice to alter dynamic arrays that have references to them?
Aug 05, 2010
simendsjo
Aug 05, 2010
simendsjo
Aug 05, 2010
simendsjo
August 05, 2010
Just tell me if this is frowned upon... The reason I posted on SO is because I think it can help making people aware of D.

http://stackoverflow.com/questions/3416657/is-it-bad-practice-to-alter-dynamic-arrays-that-have-references-to-them

Let me know if it's not accepted to crosspost like this.
August 05, 2010
On Thu, 05 Aug 2010 13:10:44 -0400, simendsjo <simen.endsjo@pandavre.com> wrote:

> Just tell me if this is frowned upon... The reason I posted on SO is because I think it can help making people aware of D.
>
> http://stackoverflow.com/questions/3416657/is-it-bad-practice-to-alter-dynamic-arrays-that-have-references-to-them

That is a very good overview of how arrays work, one thing you are missing is this:

auto a = [1,2,3];
a.length = 4;

Due to the implementation of the runtime, the a.length = x line reallocates the array.  This is because the minimum size of an array is 15 bytes (16 bytes + 1 byte to store the "used" length).

So the first line allocates an integer array in a 15-byte block, but appending one more 4-byte integer makes it grow to a 31-byte block, which means a realloc.  Note that this is implementation-defined behavior, so you should not rely on this.

Your confusion comes from expecting that the cause of a moving is because of the slice or because of b.

In answer to your question, it is fine to keep multiple references to arrays, to slice arrays and keep multiple slices to the array, and to modify the array through those references or slices.  There is a lot of code which depends on these properties to write extremely high-performing code.  I would say you will run into somewhat puzzling behavior if you are appending to an array *and* changing the original data and expect to have all the references update automatically.  While it's not illegal or even bad to do so (the runtime ensures no stomping, and ensures that all references remain valid), your program logic may fail because the decision to reallocate is implementation-defined.

One helpful function to note is the capacity function:

http://digitalmars.com/d/2.0/phobos/object.html#capacity

This gives you the capacity of the array, or the largest length it can be set to without reallocating.  If it's 0, that means any append will reallocate.  This can allow deterministic behavior when appending.  Or you can just dup the array if you want to ensure you don't mess up the original copy.

>
> Let me know if it's not accepted to crosspost like this.

I think there is no problem with that.

-Steve
August 05, 2010
On 05.08.2010 19:35, Steven Schveighoffer wrote:
> On Thu, 05 Aug 2010 13:10:44 -0400, simendsjo
> <simen.endsjo@pandavre.com> wrote:
(...)
>
> One helpful function to note is the capacity function:
>
> http://digitalmars.com/d/2.0/phobos/object.html#capacity
>
> This gives you the capacity of the array, or the largest length it can
> be set to without reallocating. If it's 0, that means any append will
> reallocate. This can allow deterministic behavior when appending. Or you
> can just dup the array if you want to ensure you don't mess up the
> original copy.

Ah, that's really good to know! So I can see if it will reallocate using
bool willReallocate = array.capacity < (array.length + numberOfItemsToAppend)
?

>>
>> Let me know if it's not accepted to crosspost like this.
>
> I think there is no problem with that.

Good, then I'll keep on doing that then.

Thanks a lot for your answer!

> -Steve

August 05, 2010
On Thu, 05 Aug 2010 14:41:12 -0400, simendsjo <simen.endsjo@pandavre.com> wrote:

> On 05.08.2010 19:35, Steven Schveighoffer wrote:
>> On Thu, 05 Aug 2010 13:10:44 -0400, simendsjo
>> <simen.endsjo@pandavre.com> wrote:
> (...)
>>
>> One helpful function to note is the capacity function:
>>
>> http://digitalmars.com/d/2.0/phobos/object.html#capacity
>>
>> This gives you the capacity of the array, or the largest length it can
>> be set to without reallocating. If it's 0, that means any append will
>> reallocate. This can allow deterministic behavior when appending. Or you
>> can just dup the array if you want to ensure you don't mess up the
>> original copy.
>
> Ah, that's really good to know! So I can see if it will reallocate using
> bool willReallocate = array.capacity < (array.length + numberOfItemsToAppend)
> ?

Yes, except the condition should be <=.  And that function is implementation independent (the value returned isn't, but it's guaranteed that willReallocate should reflect whether the runtime will reallocate)

>>>
>>> Let me know if it's not accepted to crosspost like this.
>>
>> I think there is no problem with that.
>
> Good, then I'll keep on doing that then.
>
> Thanks a lot for your answer!

No problem!

-Steve
August 05, 2010
On 05.08.2010 20:50, Steven Schveighoffer wrote:
> On Thu, 05 Aug 2010 14:41:12 -0400, simendsjo
> <simen.endsjo@pandavre.com> wrote:
>
>> On 05.08.2010 19:35, Steven Schveighoffer wrote:
>>> On Thu, 05 Aug 2010 13:10:44 -0400, simendsjo
>>> <simen.endsjo@pandavre.com> wrote:
>> (...)
>>>
(...)
>> Ah, that's really good to know! So I can see if it will reallocate using
>> bool willReallocate = array.capacity < (array.length +
>> numberOfItemsToAppend)
>> ?
>
> Yes, except the condition should be <=. And that function is
> implementation independent (the value returned isn't, but it's
> guaranteed that willReallocate should reflect whether the runtime will
> reallocate)
>
	bool willReallocate(T)(T[] array, T[] toAppend) {
		return array.capacity < (array.length + toAppend.length);
	}
	
	int[] a = [0];
	auto aOldPtr = a.ptr;
	assert(a.capacity == 3);
	
	// a.length == a.capacity, so no reallocation
	int[] b = [1,2];
	assert(!willReallocate(a, b));
	a ~= b;
	assert(a.ptr == aOldPtr);
	
	// c.length == 0, so still no
	int[] c;
	assert(!willReallocate(a, c));
	a ~= c;
	assert(a.ptr == aOldPtr);
	
	// there, a.length > a.capacity and reallocation
	int[] d = [3];
	assert(willReallocate(a, d));
	a ~= d;
	assert(a.ptr != aOldPtr);


August 05, 2010
On Thu, 05 Aug 2010 15:23:13 -0400, simendsjo <simen.endsjo@pandavre.com> wrote:

> On 05.08.2010 20:50, Steven Schveighoffer wrote:
>> On Thu, 05 Aug 2010 14:41:12 -0400, simendsjo
>> <simen.endsjo@pandavre.com> wrote:
>>
>>> On 05.08.2010 19:35, Steven Schveighoffer wrote:
>>>> On Thu, 05 Aug 2010 13:10:44 -0400, simendsjo
>>>> <simen.endsjo@pandavre.com> wrote:
>>> (...)
>>>>
> (...)
>>> Ah, that's really good to know! So I can see if it will reallocate using
>>> bool willReallocate = array.capacity < (array.length +
>>> numberOfItemsToAppend)
>>> ?
>>
>> Yes, except the condition should be <=. And that function is
>> implementation independent (the value returned isn't, but it's
>> guaranteed that willReallocate should reflect whether the runtime will
>> reallocate)
>>
> 	bool willReallocate(T)(T[] array, T[] toAppend) {
> 		return array.capacity < (array.length + toAppend.length);
> 	}
> 	
> 	int[] a = [0];
> 	auto aOldPtr = a.ptr;
> 	assert(a.capacity == 3);
> 	
> 	// a.length == a.capacity, so no reallocation
> 	int[] b = [1,2];
> 	assert(!willReallocate(a, b));
> 	a ~= b;
> 	assert(a.ptr == aOldPtr);
> 	
> 	// c.length == 0, so still no
> 	int[] c;
> 	assert(!willReallocate(a, c));
> 	a ~= c;
> 	assert(a.ptr == aOldPtr);
> 	
> 	// there, a.length > a.capacity and reallocation
> 	int[] d = [3];
> 	assert(willReallocate(a, d));
> 	a ~= d;
> 	assert(a.ptr != aOldPtr);

Oh right, for some reason, my brain did a fantastic translation of your statement to:

bool willNOTReallocate = array.length + numberOfItemsToAppend < array.capacity;

Which I then pointed out should really be <=

Sorry :)

-Steve