RK Sentinel wrote:
Thanks for all the helpful replies. Its my first venture: a widget
library.
Here’s an example: Sometimes a string is passed to a class, say, using
its set_buffer method (which is an optional method).
set_buffer just assigns it to @buffer. But deep within the class this
variable is being edited using insert() or slice!() (and this IS
necessary) since the widget is an editing widget.
Thanks, so I guess the API is something like this:
edit_field.buffer = “foo”
… some time later, after user has clicked OK …
f.write(edit_field.buffer)
Now, if there is a compelling reason for this object to perform
“in-place” editing on the buffer then by all means do, and document
this, but it will lead to the aliasing problems you describe.
It may be simpler and safer just to use non-destructive methods inside
your class.
destructive
@buffer.slice!(x,y)
non-destructive alternative
@buffer = @buffer.slice(x,y)
destructive
@buffer.insert(pos, text)
non-destructive alternative
@buffer = buffer[0,pos] + text + buffer[pos…-1]
In effect, this is doing a ‘dup’ each time. It has to; since Ruby
doesn’t do reference-counting it has no idea whether any other object in
the system is holding a reference to the original object or not.
The only problem with this is if @buffer is a multi-megabyte object and
you don’t want to keep copying it. In this case, doing a single dup
up-front would allow you to use the destructive methods safely.
class Editor
def buffer
@buffer
end
def buffer=(x)
@buffer = x.dup
end
end
The overhead of a single copy is small, and in any case this is probably
what is needed here (e.g. if the user makes some edits but clicks
‘cancel’ instead of ‘save’ then you may want to keep the old string
untouched)
You could try deferring the dup until the first time you call a
destructive method on the string, but the complexity overhead is
unlikely to be worth it.