"brixen (Brian Ford)" <brixen / gmail.com> wrote:
> There is no other Ruby core class that causes aliasing when calling
> #dup. String#dup, for example, is called in code precisely to create a
> new String so the original would not be mutated.

No other Ruby core class is a delegate for OS-level objects, either.

> That IO and StringIO do cause this aliasing is a deviation from the
> typical behavior of #dup, makes no sense, and is not at all required.
> It's perfectly possible to do the following:

It makes sense when I look at it this way:

	class Foo # this could be a numeric FD or IO object
	  attr_reader :array
	  def initialize
	    @array = [] # the underlying file handle in the kernel
	  end
	end

	a = Foo.new
	b = a.dup
	p(a.array.object_id == b.array.object_id) # => true

> sasha:rubinius brian$ cat foobar.txt 
> 123456
> sasha:rubinius brian$ irb
> 1.9.3p286 :001 > a = File.open("foobar.txt", "r")
>  => #<File:foobar.txt> 
> 1.9.3p286 :002 > b = File.open("foobar.txt", "r")
>  => #<File:foobar.txt> 

It's impossible to implement File#dup using open() reliably.  foobar.txt
can be unlinked or replaced by an entirely different file in between the
File.open calls.

> 1.9.3p286 :003 > a.getc
>  => "1" 
> 1.9.3p286 :004 > a.pos
>  => 1 
> 1.9.3p286 :005 > b.pos
>  => 0 
> 1.9.3p286 :006 > b.getc
>  => "1" 

However, if you want separate offsets, you can still use dup() but
always implement read/write using pread()/pwrite() (and manually
maintain per-object offsets in userspace).