easy_rust/Chapter_64.html

480 lines
33 KiB
HTML
Raw Normal View History

<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title> Using files - Easy Rust</title>
<!-- Custom HTML head -->
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff" />
<link rel="icon" href="favicon.svg">
<link rel="shortcut icon" href="favicon.png">
<link rel="stylesheet" href="css/variables.css">
<link rel="stylesheet" href="css/general.css">
<link rel="stylesheet" href="css/chrome.css">
<link rel="stylesheet" href="css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="highlight.css">
<link rel="stylesheet" href="tomorrow-night.css">
<link rel="stylesheet" href="ayu-highlight.css">
<!-- Custom theme stylesheets -->
</head>
<body>
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script type="text/javascript">
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script type="text/javascript">
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
</script>
<!-- Hide / unhide sidebar before it is displayed -->
<script type="text/javascript">
var html = document.querySelector('html');
var sidebar = 'hidden';
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
}
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="chapter-item expanded "><a href="Chapter_0.html"><strong aria-hidden="true">1.</strong> Update</a></li><li class="chapter-item expanded "><a href="Chapter_1.html"><strong aria-hidden="true">2.</strong> Introduction</a></li><li class="chapter-item expanded "><a href="Chapter_2.html"><strong aria-hidden="true">3.</strong> Who am I?</a></li><li class="chapter-item expanded "><a href="Chapter_3.html"><strong aria-hidden="true">4.</strong> Writing Rust in Easy English</a></li><li class="chapter-item expanded "><a href="Chapter_4.html"><strong aria-hidden="true">5.</strong> Rust Playground</a></li><li class="chapter-item expanded "><a href="Chapter_5.html"><strong aria-hidden="true">6.</strong> 🚧 and ⚠️</a></li><li class="chapter-item expanded "><a href="Chapter_6.html"><strong aria-hidden="true">7.</strong> Comments</a></li><li class="chapter-item expanded "><a href="Chapter_7.html"><strong aria-hidden="true">8.</strong> Types</a></li><li class="chapter-item expanded "><a href="Chapter_8.html"><strong aria-hidden="true">9.</strong> Type inference</a></li><li class="chapter-item expanded "><a href="Chapter_9.html"><strong aria-hidden="true">10.</strong> Printing 'hello, world!'</a></li><li class="chapter-item expanded "><a href="Chapter_10.html"><strong aria-hidden="true">11.</strong> Display and debug</a></li><li class="chapter-item expanded "><a href="Chapter_11.html"><strong aria-hidden="true">12.</strong> Mutability (changing)</a></li><li class="chapter-item expanded "><a href="Chapter_12.html"><strong aria-hidden="true">13.</strong> The stack, the heap, and pointers</a></li><li class="chapter-item expanded "><a href="Chapter_13.html"><strong aria-hidden="true">14.</strong> More about printing</a></li><li class="chapter-item expanded "><a href="Chapter_14.html"><strong aria-hidden="true">15.</strong> Strings</a></li><li class="chapter-item expanded "><a href="Chapter_15.html"><strong aria-hidden="true">16.</strong> const and static</a></li><li class="chapter-item expanded "><a href="Chapter_16.html"><strong aria-hidden="true">17.</strong> More on references</a></li><li class="chapter-item expanded "><a href="Chapter_17.html"><strong aria-hidden="true">18.</strong> Mutable references</a></li><li class="chapter-item expanded "><a href="Chapter_18.html"><strong aria-hidden="true">19.</strong> Giving references to functions</a></li><li class="chapter-item expanded "><a href="Chapter_19.html"><strong aria-hidden="true">20.</strong> Copy types</a></li><li class="chapter-item expanded "><a href="Chapter_20.html"><strong aria-hidden="true">21.</strong> Collection types</a></li><li class="chapter-item expanded "><a href="Chapter_21.html"><strong aria-hidden="true">22.</strong> Vectors</a></li><li class="chapter-item expanded "><a href="Chapter_22.html"><strong aria-hidden="true">23.</strong> Tuples</a></li><li class="chapter-item expanded "><a href="Chapter_23.html"><strong aria-hidden="true">24.</strong> Control flow</a></li><li class="chapter-item expanded "><a href="Chapter_24.html"><strong aria-hidden="true">25.</strong> Structs</a></li><li class="chapter-item expanded "><a href="Chapter_25.html"><strong aria-hidden="true">26.</strong> Enums</a></li><li class="chapter-item expanded "><a href="Chapter_26.html"><strong aria-hidden="true">27.</strong> Loops</a></li><li class="chapter-item expanded "><a href="Chapter_27.html"><strong aria-hidden="true">28.</strong> Implementing structs and enums</a></li><li class="chapter-item expanded "><a href="Chapter_28.html"><strong aria-hidden="true">29.</strong> Destructuring</a></li><li class="chapter-item expanded "><a href="Chapter_29.html"><strong aria-hidden="true">30.</strong> References and the dot operator</a></li><li class="chapter-item expanded "><a href="Chapter_30.html"><strong aria-hidden="true">31.</strong> Generics</a></li><li class="chapter-item expanded "><a href="Chapter_31.html"><strong aria-hidden="true">32.</strong> Option and Result</a></li><li class="chapter-item expanded "><a href="Chapter_32.html"><strong aria-hi
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky bordered">
<div class="left-buttons">
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</button>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Easy Rust</h1>
<div class="right-buttons">
<a href="print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script type="text/javascript">
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h2 id="using-files"><a class="header" href="#using-files">Using files</a></h2>
<p>Now that we are using Rust on the computer, we can start working with files. You will notice that now we will start to see more and more <code>Result</code>s in our code. That is because once you start working with files and similar things, many things can go wrong. A file might not be there, or maybe the computer can't read it.</p>
<p>You might remember that if you want to use the <code>?</code> operator, it has to return a <code>Result</code> in the function it is in. If you can't remember the error type, you can just give it nothing and let the compiler tell you. Let's try that with a function that tries to make a number with <code>.parse()</code>.</p>
<pre><pre class="playground"><code class="language-rust">// ⚠️
fn give_number(input: &amp;str) -&gt; Result&lt;i32, ()&gt; {
input.parse::&lt;i32&gt;()
}
fn main() {
println!(&quot;{:?}&quot;, give_number(&quot;88&quot;));
println!(&quot;{:?}&quot;, give_number(&quot;5&quot;));
}
</code></pre></pre>
<p>The compiler tells us exactly what to do:</p>
<pre><code class="language-text">error[E0308]: mismatched types
--&gt; src\main.rs:4:5
|
3 | fn give_number(input: &amp;str) -&gt; Result&lt;i32, ()&gt; {
| --------------- expected `std::result::Result&lt;i32, ()&gt;` because of return type
4 | input.parse::&lt;i32&gt;()
| ^^^^^^^^^^^^^^^^^^^^ expected `()`, found struct `std::num::ParseIntError`
|
= note: expected enum `std::result::Result&lt;_, ()&gt;`
found enum `std::result::Result&lt;_, std::num::ParseIntError&gt;`
</code></pre>
<p>Great! So we just change the return to that. Now it works:</p>
<pre><pre class="playground"><code class="language-rust">use std::num::ParseIntError;
fn give_number(input: &amp;str) -&gt; Result&lt;i32, ParseIntError&gt; {
input.parse::&lt;i32&gt;()
}
fn main() {
println!(&quot;{:?}&quot;, give_number(&quot;88&quot;));
println!(&quot;{:?}&quot;, give_number(&quot;5&quot;));
}
</code></pre></pre>
<p>The first one doesn't work, but the second one does.</p>
<pre><code class="language-text">Ok(88)
Ok(5)
</code></pre>
<p>So now we want to use <code>?</code> to just give us the value if it works, and the error if it doesn't. But how to do this in <code>fn main()</code>? If we try to use <code>?</code> in main, it won't work.</p>
<pre><pre class="playground"><code class="language-rust">// ⚠️
use std::num::ParseIntError;
fn give_number(input: &amp;str) -&gt; Result&lt;i32, ParseIntError&gt; {
input.parse::&lt;i32&gt;()
}
fn main() {
println!(&quot;{:?}&quot;, give_number(&quot;88&quot;)?);
println!(&quot;{:?}&quot;, give_number(&quot;5&quot;)?);
}
</code></pre></pre>
<p>It says:</p>
<pre><code class="language-text">error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
--&gt; src\main.rs:8:22
|
7 | / fn main() {
8 | | println!(&quot;{:?}&quot;, give_number(&quot;88&quot;)?);
| | ^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
9 | | println!(&quot;{:?}&quot;, give_number(&quot;5&quot;)?);
10 | | }
| |_- this function should return `Result` or `Option` to accept `?`
</code></pre>
<p>But actually <code>main()</code> can return a <code>Result</code>, just like any other function. If our function works, we don't want to return anything (main() isn't giving anything to anything else). And if it doesn't work, we will return the same error. So we can write it like this:</p>
<pre><pre class="playground"><code class="language-rust">use std::num::ParseIntError;
fn give_number(input: &amp;str) -&gt; Result&lt;i32, ParseIntError&gt; {
input.parse::&lt;i32&gt;()
}
fn main() -&gt; Result&lt;(), ParseIntError&gt; {
println!(&quot;{:?}&quot;, give_number(&quot;88&quot;)?);
println!(&quot;{:?}&quot;, give_number(&quot;5&quot;)?);
Ok(())
}
</code></pre></pre>
<p>Don't forget the <code>Ok(())</code> at the end: this is very common in Rust. It means <code>Ok</code>, inside of which is <code>()</code>, which is our return value. Now it prints:</p>
<pre><code class="language-text">88
5
</code></pre>
<p>This wasn't very useful when just using <code>.parse()</code>, but it will be with files. That's because <code>?</code> also changes error types for us. Here's what <a href="https://doc.rust-lang.org/std/macro.try.html">the page for the ? operator</a> says in simple English:</p>
<pre><code class="language-text">If you get an `Err`, it will get the inner error. Then `?` does a conversion using `From`. With that it can change specialized errors to more general ones. The error it gets is then returned.
</code></pre>
<p>Also, Rust has a convenient <code>Result</code> type when using <code>File</code>s and similar things. It's called <code>std::io::Result</code>, and this is what you usually see in <code>main()</code> when you are using <code>?</code> to open and do things to files. It's actually a type alias. It looks like this:</p>
<pre><code class="language-text">type Result&lt;T&gt; = Result&lt;T, Error&gt;;
</code></pre>
<p>So it is a <code>Result&lt;T, Error&gt;</code>, but we only need to write the <code>Result&lt;T&gt;</code> part.</p>
<p>Now let's try working with files for the first time. <code>std::fs</code> is where the methods are for working with files, and with <code>std::io::Write</code> you can write in them. With that we can use <code>.write_all()</code> to write into the file.</p>
<pre><pre class="playground"><code class="language-rust">use std::fs;
use std::io::Write;
fn main() -&gt; std::io::Result&lt;()&gt; {
let mut file = fs::File::create(&quot;myfilename.txt&quot;)?; // Create a file with this name.
// CAREFUL! If you have a file with this name already,
// it will delete it and make a new one.
file.write_all(b&quot;Let's put this in the file&quot;)?; // Don't forget the b in front of &quot;. That's because files take bytes.
Ok(())
}
</code></pre></pre>
<p>Then if you click on the new file <code>myfilename.txt</code>, it will say <code>Let's put this in the file</code>.</p>
<p>We don't need to do this on two lines though, because we have the <code>?</code> operator. It will pass on the result we want if it works, kind of like when you use lots of methods on an iterator. This is when <code>?</code> becomes very convenient.</p>
<pre><pre class="playground"><code class="language-rust">use std::fs;
use std::io::Write;
fn main() -&gt; std::io::Result&lt;()&gt; {
fs::File::create(&quot;myfilename.txt&quot;)?.write_all(b&quot;Let's put this in the file&quot;)?;
Ok(())
}
</code></pre></pre>
<p>So this is saying &quot;Please try to create a file and check if it worked. If it did, then use <code>.write_all()</code> and then check if that worked.&quot;</p>
<p>And in fact, there is also a function that does both of these things together. It's called <code>std::fs::write</code>. Inside it you give it the file name you want, and the content you want to put inside. Again, careful! It will delete any file that's already there if it has the same name. Also, it lets you write a <code>&amp;str</code> without <code>b</code> in front, because of this:</p>
<pre><pre class="playground"><code class="language-rust">
<span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub fn write&lt;P: AsRef&lt;Path&gt;, C: AsRef&lt;[u8]&gt;&gt;(path: P, contents: C) -&gt; Result&lt;()&gt;
<span class="boring">}
</span></code></pre></pre>
<p><code>AsRef&lt;[u8]&gt;</code> is why you can give it either one.</p>
<p>It's very simple:</p>
<pre><pre class="playground"><code class="language-rust">use std::fs;
fn main() -&gt; std::io::Result&lt;()&gt; {
fs::write(&quot;calvin_with_dad.txt&quot;,
&quot;Calvin: Dad, how come old photographs are always black and white? Didn't they have color film back then?
Dad: Sure they did. In fact, those photographs *are* in color. It's just the *world* was black and white then.
Calvin: Really?
Dad: Yep. The world didn't turn color until sometimes in the 1930s...&quot;)?;
Ok(())
}
</code></pre></pre>
<p>So that's the file we will use. It's a conversation with a comic book character named Calvin and his dad, who is not serious about his question. With this we can create a file to use every time.</p>
<p>Opening a file is just as easy as creating one. You just use <code>open()</code> instead of <code>create()</code>. After that (if it finds your file), you can do things like <code>read_to_string()</code>. To do that you can create a mutable <code>String</code> and read the file into there. It looks like this:</p>
<pre><pre class="playground"><code class="language-rust">use std::fs;
use std::fs::File;
use std::io::Read; // this is to use the function .read_to_string()
fn main() -&gt; std::io::Result&lt;()&gt; {
fs::write(&quot;calvin_with_dad.txt&quot;,
&quot;Calvin: Dad, how come old photographs are always black and white? Didn't they have color film back then?
Dad: Sure they did. In fact, those photographs *are* in color. It's just the *world* was black and white then.
Calvin: Really?
Dad: Yep. The world didn't turn color until sometimes in the 1930s...&quot;)?;
let mut calvin_file = File::open(&quot;calvin_with_dad.txt&quot;)?; // Open the file we just made
let mut calvin_string = String::new(); // This String will hold it
calvin_file.read_to_string(&amp;mut calvin_string)?; // Read the file into it
calvin_string.split_whitespace().for_each(|word| print!(&quot;{} &quot;, word.to_uppercase())); // Do things with the String now
Ok(())
}
</code></pre></pre>
<p>That will print:</p>
<pre><pre class="playground"><code class="language-rust">
<span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>CALVIN: DAD, HOW COME OLD PHOTOGRAPHS ARE ALWAYS BLACK AND WHITE? DIDN'T THEY HAVE COLOR FILM BACK THEN? DAD: SURE THEY DID. IN
FACT, THOSE PHOTOGRAPHS *ARE* IN COLOR. IT'S JUST THE *WORLD* WAS BLACK AND WHITE THEN. CALVIN: REALLY? DAD: YEP. THE WORLD DIDN'T TURN COLOR UNTIL SOMETIMES IN THE 1930S...
<span class="boring">}
</span></code></pre></pre>
<p>Okay, what if we want to create a file but not do it if there is already another file with the same name? Maybe you don't want to delete the other file if it's already there just to make a new one. To do this, there is a struct called <code>OpenOptions</code>. Actually, we've been using <code>OpenOptions</code> all this time and didn't know it. Take a look at the source for <code>File::open</code>:</p>
<pre><pre class="playground"><code class="language-rust">
<span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub fn open&lt;P: AsRef&lt;Path&gt;&gt;(path: P) -&gt; io::Result&lt;File&gt; {
OpenOptions::new().read(true).open(path.as_ref())
}
<span class="boring">}
</span></code></pre></pre>
<p>Interesting, that looks like the builder pattern that we learned. It's the same for <code>File::create</code>:</p>
<pre><pre class="playground"><code class="language-rust">
<span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub fn create&lt;P: AsRef&lt;Path&gt;&gt;(path: P) -&gt; io::Result&lt;File&gt; {
OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref())
}
<span class="boring">}
</span></code></pre></pre>
<p>If you go to <a href="https://doc.rust-lang.org/std/fs/struct.OpenOptions.html">the page for OpenOptions</a>, you can see all the methods that you can choose from. Most take a <code>bool</code>:</p>
<ul>
<li><code>append()</code>: This means &quot;add to the content that's already there instead of deleting&quot;.</li>
<li><code>create()</code>: This lets <code>OpenOptions</code> create a file.</li>
<li><code>create_new()</code>: This means it will only create a file if it's not there already.</li>
<li><code>read()</code>: Set this to <code>true</code> if you want it to be able to read a file.</li>
<li><code>truncate()</code>: Set this to true if you want to cut the file content to 0 (delete the contents) when you open it.</li>
<li><code>write()</code>: This lets it write to a file.</li>
</ul>
<p>Then at the end you use <code>.open()</code> with the file name, and that will give you a <code>Result</code>. Let's look at one example:</p>
<pre><pre class="playground"><code class="language-rust">// ⚠️
use std::fs;
use std::fs::OpenOptions;
fn main() -&gt; std::io::Result&lt;()&gt; {
fs::write(&quot;calvin_with_dad.txt&quot;,
&quot;Calvin: Dad, how come old photographs are always black and white? Didn't they have color film back then?
Dad: Sure they did. In fact, those photographs *are* in color. It's just the *world* was black and white then.
Calvin: Really?
Dad: Yep. The world didn't turn color until sometimes in the 1930s...&quot;)?;
let calvin_file = OpenOptions::new().write(true).create_new(true).open(&quot;calvin_with_dad.txt&quot;)?;
Ok(())
}
</code></pre></pre>
<p>First we made an <code>OpenOptions</code> with <code>new</code> (always start with <code>new</code>). Then we gave it the ability to <code>write</code>. After that we set <code>create_new()</code> to <code>true</code>, and tried to open the file we made. It won't work, which is what we want:</p>
<pre><code class="language-text">Error: Os { code: 80, kind: AlreadyExists, message: &quot;The file exists.&quot; }
</code></pre>
<p>Let's try using <code>.append()</code> so we can write to a file. To write to the file we can use <code>.write_all()</code>, which is a method that tries to write in everything you give it.</p>
<p>Also, we will use the <code>write!</code> macro to do the same thing. You will remember this macro from when we did <code>impl Display</code> for our structs. This time we are using it on a file though instead of a buffer.</p>
<pre><pre class="playground"><code class="language-rust">use std::fs;
use std::fs::OpenOptions;
use std::io::Write;
fn main() -&gt; std::io::Result&lt;()&gt; {
fs::write(&quot;calvin_with_dad.txt&quot;,
&quot;Calvin: Dad, how come old photographs are always black and white? Didn't they have color film back then?
Dad: Sure they did. In fact, those photographs *are* in color. It's just the *world* was black and white then.
Calvin: Really?
Dad: Yep. The world didn't turn color until sometimes in the 1930s...&quot;)?;
let mut calvin_file = OpenOptions::new()
.append(true) // Now we can write without deleting it
.read(true)
.open(&quot;calvin_with_dad.txt&quot;)?;
calvin_file.write_all(b&quot;And it was a pretty grainy color for a while too.\n&quot;)?;
write!(&amp;mut calvin_file, &quot;That's really weird.\n&quot;)?;
write!(&amp;mut calvin_file, &quot;Well, truth is stranger than fiction.&quot;)?;
println!(&quot;{}&quot;, fs::read_to_string(&quot;calvin_with_dad.txt&quot;)?);
Ok(())
}
</code></pre></pre>
<p>This prints:</p>
<pre><code class="language-text">Calvin: Dad, how come old photographs are always black and white? Didn't they have color film back then?
Dad: Sure they did. In fact, those photographs *are* in color. It's just the *world* was black and white then.
Calvin: Really?
Dad: Yep. The world didn't turn color until sometimes in the 1930s...And it was a pretty grainy color for a while too.
That's really weird.
Well, truth is stranger than fiction.
</code></pre>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="Chapter_63.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next" href="Chapter_65.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="Chapter_63.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next" href="Chapter_65.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script type="text/javascript">
window.playground_copyable = true;
</script>
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
<script src="book.js" type="text/javascript" charset="utf-8"></script>
<!-- Custom JS scripts -->
</body>
</html>