Difference between $stdout and STDOUT in Ruby
$stdout
is a global variable that represents the current standard output. STDOUT
is a constant representing standard output and is typically the default value of $stdout
.
With STDOUT
being a constant, you shouldn't re-define it, however, you can re-define $stdout
without errors/warnings (re-defining STDOUT
will raise a warning). for example, you can do:
$stdout = STDERR
Same goes for $stderr
and STDERR
So, to answer the other part of your question, use the global variables to redirect output, not the constants. Just be careful to change it back further on in your code, re-defining global variables can impact other parts of your application.
What is the difference between STDIN and $stdin in Ruby?
If $stdin
is reassigned, STDIN
is not affected. Likewise $stdin
is not affected when STDIN
is reassigned (which is perfectly possible (though pointless), but will produce a warning). However if neither variable has been reassigned, they both point to the same IO object, so calling reopen
¹ on one will affect the other.
All the built-in ruby methods use $<
(a.k.a. ARGF
) to read input. If ARGV
is empty, ARGF
reads from $stdin
, so if you reassign $stdin
, that will affect all built-in methods. If you reassign STDIN
it will have no effect unless some 3rd party method uses STDIN
.
In your own code you should use $stdin
to be consistent with the built-in methods².
¹ reopen
is a method which can redirect an IO object to another stream or file. However you can't use it to redirect an IO to a StringIO, so it does not eliminate all uses cases of reassigning $stdin
.
² You may of course also use $<
/ARGF
to be even more consistent with the built-in methods, but most of the time you don't want the ARGF
behavior if you're explicitly using the stdin stream.
Understanding how ruby method $stdout works
$stdout
is a global variable. By default it stores an object of type IO
associated with the standard output of the program (which is, by default, the console).
puts
is a method of the Kernel
module that actually calls $stdout.send()
and pass it the list of arguments it receives. As the documentation explains, puts(obj, ...)
is equivalent to $stdout.puts(obj, ...)
.
Your code replaces $stdout
with an object of type File
that extends class IO
. When it is created, your object opens the file out.txt
for writing and together with its inheritance from IO
it is fully compatible with the default behaviour of $stdout
.
Since by default, all the output goes to $stdout
, your new definition of $stdout
ensures the output is written to the file out.txt
without other changes in the code.
Why do Global Variables exist in Ruby?
Global variables are not bad. They're not evil. They're just incredibly, incredibly powerful. Which is why you shouldn't use them.
Global variables are global- they can be accessed and modified anywhere in the code. A single global variable has the potential to affect all of your classes, all of your functions, all of the classes and functions of every single library or dependency you load into your project, and all of the classes and functions of every single project which loads your project as a dependency, as well as the projects that load those projects, and so and and so forth, for ever and always, for the rest of time.
The second people start feeling comfortable using global variables, the namespace gets insanely cluttered and we get conflicts left and right and the stability of the programming language itself is threatened. Which is why the use of global variables is emphatically and repeatedly discouraged.
But global variables are not bad. They're like the highway lanes labeled "for emergency vehicles only," or like those fire-axes behind glass labeled "break glass in case of emergency."
It's entirely possible that at some point, in the distant future, you will have an incredibly unusual situation which merits the use of a single global variable. But that day is not today. And it is probably not tomorrow, or a month from now, or a year from now. Daily life, daily code- it just doesn't call for the unbridled power of a global variable.
$stdout
is a great example of why global variables are sometimes important. $stdout
is the default stream in ruby- the one where things will print if no other stream is specified. $stdout
should be accessible from every class and every function in every library because it acts like a giant funnel, shoveling all output to a single location. The whole world knows and agrees that $stdout
exists in ruby, and its uses are well-documented, so its power is well-managed.
This isn't to be confused with STDOUT
which is a constant representing the actual pipe which sets up a stream between ruby and its parent program (usually a terminal). $stdout = STDOUT
by default, but $stdout
can be changed to anything. If you want your program to print to a file, you can change $stdout
to a file stream.
I don't think this name choice is confusing for a seasoned rubyist. A variable is designed to be modified and a constant is designed to be constant. The difference between $stdout and STDOUT is that the former can be modified to change the standard output location of your program, and the latter is a constant, always pointing to the stdout stream. The capitalization makes a world of difference and conveys very different meanings.
As for why global constants are uninitialized and global variables are nil
, that actually has nothing to do with globals. Ruby automatically initializes all variables as nil
. You can easily see this with instance variables such as @foo
or @@foo
. In almost every situation, an undefined local variable will throw a NameError
because ruby cannot tell whether it is a variable or a method. But in strange situations, they too are initialized as nil
:
puts foo # => NameError: undefined local variable or method 'foo'
foo = 42 if false
puts foo # => nil
puts bar # => NameError
bar = bar
puts bar # => nil
It was a conscious design choice in Ruby not to automatically initialize constants. Because a constant is, by definition, something which is initialized once and then never changed, it would break the definition for a constant to be nil
at first and then a different value later in the code.
I should also mention that global constants are considered acceptable, even among people who tout global variables as bad. The difference is that constants can only be assigned once and generally throw a warning or error if they get assigned again. This protects programmers from situations where conflicting global constants might cause problems.
What happens to the object I assign to $stdout in Ruby?
Are you sure there is no other manipulation of $stdout
or capture
happening in between?
For me, output looks different. Both capture
and $stdout
are the same object and subsequently answer to string
with the same response (ruby 1.9.2):
require 'stringio'
$stdout.to_s # => #<IO:0x2584b30>
capture = StringIO.new
$stdout = capture
puts $stdout.to_s # => #<StringIO:0x89a38c0>
puts capture.to_s # => #<StringIO:0x89a38c0>
puts "redirected"
$stderr.puts $stdout.string # => '#<StringIO:0x89a38c0>\n#<StringIO:0x89a38c0>\nredirected'
$stderr.puts capture.string # => '#<StringIO:0x89a38c0>\n#<StringIO:0x89a38c0>\nredirected'
Related Topics
How to Use Bundler Behind a Proxy
Undefined Method Raise_In_Transactional_Callbacks=' for Activerecord::Base:Class (Nomethoderror)
To Use Self. or Not.. in Rails
Cannot Use Rvm-Installed Ruby With Sudo
Validation Failed: Upload File Has an Extension That Does Not Match Its Contents
Looping in a Spiral Outside-In
Cron Job Not Working in Whenever Gem
Ruby - Difference Between Array#≪≪ and Array#Push
Best Practices to Handle Routes For Sti Subclasses in Rails
When Do I Need to Restart Server in Rails
How to Use "Gets" and "Gets.Chomp" in Ruby
Rails Installation Failed on Ubuntu With "Cannot Load Such File - Mkmf"
Google Plus API Shutdown Today, Which Alternative Can Be Used to Authentication
Test If Variable Matches Any of Several Strings W/O Long If-Elsif Chain, or Case-When