mirror of
https://github.com/rust-lang-cn/book-cn.git
synced 2025-01-23 07:20:20 +08:00
Merge pull request #58 from rust-lang/ownership
first draft of 'ownership'
This commit is contained in:
commit
1de1dd76ea
24
dot/foo1.dot
Normal file
24
dot/foo1.dot
Normal file
@ -0,0 +1,24 @@
|
||||
digraph {
|
||||
rankdir=LR;
|
||||
node [shape="plaintext"];
|
||||
|
||||
table0[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD COLSPAN="2" SIDES="B">s1</TD></TR>
|
||||
<TR><TD>name</TD><TD>value</TD></TR>
|
||||
<TR><TD>ptr</TD><TD PORT="pointer"></TD></TR>
|
||||
<TR><TD>len</TD><TD>5</TD></TR>
|
||||
<TR><TD>capacity</TD><TD>5</TD></TR>
|
||||
</TABLE>>];
|
||||
table1[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD>index</TD><TD>value</TD></TR>
|
||||
<TR><TD PORT="pointee">0</TD><TD>h</TD></TR>
|
||||
<TR><TD>1</TD><TD>e</TD></TR>
|
||||
<TR><TD>2</TD><TD>l</TD></TR>
|
||||
<TR><TD>3</TD><TD>l</TD></TR>
|
||||
<TR><TD>4</TD><TD>o</TD></TR>
|
||||
</TABLE>>];
|
||||
|
||||
edge[tailclip="false"];
|
||||
table0:pointer:c -> table1:pointee;
|
||||
}
|
||||
|
33
dot/foo2.dot
Normal file
33
dot/foo2.dot
Normal file
@ -0,0 +1,33 @@
|
||||
digraph {
|
||||
rankdir=LR;
|
||||
node [shape="plaintext"];
|
||||
|
||||
table0[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD COLSPAN="2" SIDES="B">s1</TD></TR>
|
||||
<TR><TD>name</TD><TD>value</TD></TR>
|
||||
<TR><TD>ptr</TD><TD PORT="pointer"></TD></TR>
|
||||
<TR><TD>len</TD><TD>5</TD></TR>
|
||||
<TR><TD>capacity</TD><TD>5</TD></TR>
|
||||
</TABLE>>];
|
||||
table3[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD COLSPAN="2" SIDES="B">s2</TD></TR>
|
||||
<TR><TD>name</TD><TD>value</TD></TR>
|
||||
<TR><TD>ptr</TD><TD PORT="pointer"></TD></TR>
|
||||
<TR><TD>len</TD><TD>5</TD></TR>
|
||||
<TR><TD>capacity</TD><TD>5</TD></TR>
|
||||
</TABLE>>];
|
||||
|
||||
table1[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD>index</TD><TD>value</TD></TR>
|
||||
<TR><TD PORT="pointee">0</TD><TD>h</TD></TR>
|
||||
<TR><TD>1</TD><TD>e</TD></TR>
|
||||
<TR><TD>2</TD><TD>l</TD></TR>
|
||||
<TR><TD>3</TD><TD>l</TD></TR>
|
||||
<TR><TD>4</TD><TD>o</TD></TR>
|
||||
</TABLE>>];
|
||||
|
||||
edge[tailclip="false"];
|
||||
table0:pointer:c -> table1:pointee;
|
||||
table3:pointer:c -> table1:pointee;
|
||||
}
|
||||
|
33
dot/foo3.dot
Normal file
33
dot/foo3.dot
Normal file
@ -0,0 +1,33 @@
|
||||
digraph {
|
||||
rankdir=LR;
|
||||
node [shape="plaintext"];
|
||||
|
||||
table0[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" BGCOLOR="gray">
|
||||
<TR><TD COLSPAN="2" SIDES="B">s1</TD></TR>
|
||||
<TR><TD>name</TD><TD>value</TD></TR>
|
||||
<TR><TD>ptr</TD><TD PORT="pointer"></TD></TR>
|
||||
<TR><TD>len</TD><TD>5</TD></TR>
|
||||
<TR><TD>capacity</TD><TD>5</TD></TR>
|
||||
</TABLE>>];
|
||||
table3[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD COLSPAN="2" SIDES="B">s2</TD></TR>
|
||||
<TR><TD>name</TD><TD>value</TD></TR>
|
||||
<TR><TD>ptr</TD><TD PORT="pointer"></TD></TR>
|
||||
<TR><TD>len</TD><TD>5</TD></TR>
|
||||
<TR><TD>capacity</TD><TD>5</TD></TR>
|
||||
</TABLE>>];
|
||||
|
||||
table1[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD>index</TD><TD>value</TD></TR>
|
||||
<TR><TD PORT="pointee">0</TD><TD>h</TD></TR>
|
||||
<TR><TD>1</TD><TD>e</TD></TR>
|
||||
<TR><TD>2</TD><TD>l</TD></TR>
|
||||
<TR><TD>3</TD><TD>l</TD></TR>
|
||||
<TR><TD>4</TD><TD>o</TD></TR>
|
||||
</TABLE>>];
|
||||
|
||||
edge[tailclip="false"];
|
||||
table0:pointer:c -> table1:pointee;
|
||||
table3:pointer:c -> table1:pointee;
|
||||
}
|
||||
|
42
dot/foo4.dot
Normal file
42
dot/foo4.dot
Normal file
@ -0,0 +1,42 @@
|
||||
digraph {
|
||||
rankdir=LR;
|
||||
node [shape="plaintext"];
|
||||
|
||||
table0[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD COLSPAN="2" SIDES="B">s1</TD></TR>
|
||||
<TR><TD>name</TD><TD>value</TD></TR>
|
||||
<TR><TD>ptr</TD><TD PORT="pointer"></TD></TR>
|
||||
<TR><TD>len</TD><TD>5</TD></TR>
|
||||
<TR><TD>capacity</TD><TD>5</TD></TR>
|
||||
</TABLE>>];
|
||||
table1[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD>index</TD><TD>value</TD></TR>
|
||||
<TR><TD PORT="pointee">0</TD><TD>h</TD></TR>
|
||||
<TR><TD>1</TD><TD>e</TD></TR>
|
||||
<TR><TD>2</TD><TD>l</TD></TR>
|
||||
<TR><TD>3</TD><TD>l</TD></TR>
|
||||
<TR><TD>4</TD><TD>o</TD></TR>
|
||||
</TABLE>>];
|
||||
|
||||
table3[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD COLSPAN="2" SIDES="B">s2</TD></TR>
|
||||
<TR><TD>name</TD><TD>value</TD></TR>
|
||||
<TR><TD>ptr</TD><TD PORT="pointer"></TD></TR>
|
||||
<TR><TD>len</TD><TD>5</TD></TR>
|
||||
<TR><TD>capacity</TD><TD>5</TD></TR>
|
||||
</TABLE>>];
|
||||
table4[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD>index</TD><TD>value</TD></TR>
|
||||
<TR><TD PORT="pointee">0</TD><TD>h</TD></TR>
|
||||
<TR><TD>1</TD><TD>e</TD></TR>
|
||||
<TR><TD>2</TD><TD>l</TD></TR>
|
||||
<TR><TD>3</TD><TD>l</TD></TR>
|
||||
<TR><TD>4</TD><TD>o</TD></TR>
|
||||
</TABLE>>];
|
||||
|
||||
|
||||
edge[tailclip="false"];
|
||||
table0:pointer:c -> table1:pointee;
|
||||
table3:pointer:c -> table4:pointee;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
- [Loops](loops.md)
|
||||
|
||||
- [Understanding Ownership](understanding-ownership.md)
|
||||
- [Ownership]()
|
||||
- [Ownership](ownership.md)
|
||||
- [References & Borrowing]()
|
||||
- [Memory Layout]()
|
||||
- [Slices]()
|
||||
|
BIN
src/img/foo1.png
Normal file
BIN
src/img/foo1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.8 KiB |
BIN
src/img/foo2.png
Normal file
BIN
src/img/foo2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
src/img/foo3.png
Normal file
BIN
src/img/foo3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
src/img/foo4.png
Normal file
BIN
src/img/foo4.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
535
src/ownership.md
535
src/ownership.md
@ -1,238 +1,461 @@
|
||||
# Ownership
|
||||
|
||||
This guide is one of three presenting Rust’s ownership system. This is one of
|
||||
Rust’s most unique and compelling features, with which Rust developers should
|
||||
become quite acquainted. Ownership is how Rust achieves its largest goal,
|
||||
memory safety. There are a few distinct concepts, each with its own
|
||||
chapter:
|
||||
Rust’s central feature is called ‘ownership’. It is a feature that is
|
||||
straightforward to explain, but has deep implications for the rest of the
|
||||
language.
|
||||
|
||||
* ownership, which you’re reading now
|
||||
* [borrowing][borrowing], and their associated feature ‘references’
|
||||
* [lifetimes][lifetimes], an advanced concept of borrowing
|
||||
|
||||
These three chapters are related, and in order. You’ll need all three to fully
|
||||
understand the ownership system.
|
||||
|
||||
[borrowing]: references-and-borrowing.html
|
||||
[lifetimes]: lifetimes.html
|
||||
|
||||
# Meta
|
||||
|
||||
Before we get to the details, two important notes about the ownership system.
|
||||
|
||||
Rust has a focus on safety and speed. It accomplishes these goals through many
|
||||
‘zero-cost abstractions’, which means that in Rust, abstractions cost as little
|
||||
as possible in order to make them work. The ownership system is a prime example
|
||||
Rust is committed to both safety and speed. One of the key tools for balancing
|
||||
between them is “zero-cost abstractions”: the various abstractions in Rust do
|
||||
not pose a global performance penalty. The ownership system is a prime example
|
||||
of a zero-cost abstraction. All of the analysis we’ll talk about in this guide
|
||||
is _done at compile time_. You do not pay any run-time cost for any of these
|
||||
is done at compile time. You do not pay any run-time cost for any of these
|
||||
features.
|
||||
|
||||
However, this system does have a certain cost: learning curve. Many new users
|
||||
to Rust experience something we like to call ‘fighting with the borrow
|
||||
However, this system does have a certain cost: learning curve. Many new
|
||||
Rustaceans experience something we like to call ‘fighting with the borrow
|
||||
checker’, where the Rust compiler refuses to compile a program that the author
|
||||
thinks is valid. This often happens because the programmer’s mental model of
|
||||
how ownership should work doesn’t match the actual rules that Rust implements.
|
||||
You probably will experience similar things at first. There is good news,
|
||||
however: more experienced Rust developers report that once they work with the
|
||||
rules of the ownership system for a period of time, they fight the borrow
|
||||
checker less and less.
|
||||
thinks is valid. This can happen because the programmer isn’t used to thinking
|
||||
carefully about ownership, or is thinking about it differently from the way
|
||||
that Rust does. You probably will experience something similar at first. There is
|
||||
good news, however: more experienced Rust developers report that once they work
|
||||
with the rules of the ownership system for a period of time, they fight the
|
||||
borrow checker less and less. Keep at it!
|
||||
|
||||
With that in mind, let’s learn about ownership.
|
||||
This chapter will give you a foundation for understanding the rest of the
|
||||
language. To do so, we’re going to learn through examples, focusing on a very
|
||||
common data structure: strings.
|
||||
|
||||
# Ownership
|
||||
## Variable binding scope
|
||||
|
||||
[Variable bindings][bindings] have a property in Rust: they ‘have ownership’
|
||||
of what they’re bound to. This means that when a binding goes out of scope,
|
||||
Rust will free the bound resources. For example:
|
||||
Let’s take a step back and look at the very basics again. Now that we’re past
|
||||
basic syntax, we won’t include all of the `fn main() {` stuff in examples, so
|
||||
if you’re following along, you will have to put them inside of a `main()`
|
||||
function. This lets our examples be a bit more concise, letting us focus on the
|
||||
actual details, rather than boilerplate.
|
||||
|
||||
Anyway, here it is:
|
||||
|
||||
```rust
|
||||
fn foo() {
|
||||
let v = vec![1, 2, 3];
|
||||
}
|
||||
let s = "hello";
|
||||
```
|
||||
|
||||
When `v` comes into scope, a new [`Vec<T>`][vect] is created. In this case, the
|
||||
vector also allocates space on [the heap][heap], for the three elements. When
|
||||
`v` goes out of scope at the end of `foo()`, Rust will clean up everything
|
||||
related to the vector, even the heap-allocated memory. This happens
|
||||
deterministically, at the end of the scope.
|
||||
|
||||
[vect]: ../std/vec/struct.Vec.html
|
||||
[heap]: the-stack-and-the-heap.html
|
||||
[bindings]: variable-bindings.html
|
||||
|
||||
# Move semantics
|
||||
|
||||
There’s some more subtlety here, though: Rust ensures that there is _exactly
|
||||
one_ binding to any given resource. For example, if we have a vector, we can
|
||||
assign it to another binding:
|
||||
This variable binding refers to a string literal. It’s valid from the point at
|
||||
which it’s declared, until the end of the current _scope_. That is:
|
||||
|
||||
```rust
|
||||
let v = vec![1, 2, 3];
|
||||
{ // s is not valid here, it’s not yet in scope
|
||||
let s = "hello"; // s is valid from this point forward
|
||||
|
||||
let v2 = v;
|
||||
// do stuff with s
|
||||
} // this scope is now over, and s is no longer valid
|
||||
```
|
||||
|
||||
But, if we try to use `v` afterwards, we get an error:
|
||||
In other words, there are two important points in time here:
|
||||
|
||||
- When `s` comes ‘into scope’, it is valid.
|
||||
- It remains so until it ‘goes out of scope’.
|
||||
|
||||
At this point, things are similar to other programming languages. Let’s build
|
||||
on top of this understanding by introducing a new type: `String`.
|
||||
|
||||
## Strings
|
||||
|
||||
String literals are convenient, but they aren’t the only way that you use strings.
|
||||
For one thing, they’re immutable. For another, not every string is literal:
|
||||
what about taking user input and storing it in a string?
|
||||
|
||||
For this, Rust has a second string type, `String`. You can create a `String` from
|
||||
a string literal using the `from` function:
|
||||
|
||||
```rust
|
||||
let s = String::from("hello");
|
||||
```
|
||||
|
||||
We haven’t seen the double colon (`::`) syntax yet. It is a kind of scope
|
||||
operator, allowing us to namespace this particular `from()` function under the
|
||||
`String` type itself, rather than using some sort of name like `string_from()`.
|
||||
We’ll discuss this syntax more in the “Method Syntax” and “Modules” chapters.
|
||||
|
||||
This kind of string can be mutated:
|
||||
|
||||
```rust
|
||||
let mut s = String::from("hello");
|
||||
|
||||
s.push_str(", world!");
|
||||
```
|
||||
|
||||
## Memory and allocation
|
||||
|
||||
So, what’s the difference here? Why can `String` be mutated, but literals
|
||||
cannot? The difference comes down to how these two types deal with memory.
|
||||
|
||||
In the case of a string literal, because we know the contents of the string at
|
||||
compile time, we can hard-code the text of the string directly into the final
|
||||
executable. This means that string literals are quite fast and efficient. But
|
||||
these properties only come from its immutability; we can’t put an
|
||||
arbitrary-sized blob of memory into the binary for each string!
|
||||
|
||||
With `String`, to support a mutable, growable string, we need to allocate an
|
||||
unknown amount of memory to hold the contents. This means two things:
|
||||
|
||||
1. The memory must be requested from the operating system at runtime.
|
||||
2. We need a way of giving this memory back to the operating system when we’re
|
||||
done with our `String`.
|
||||
|
||||
That first part is done by us: when we call `String::from()`, its
|
||||
implementation requests the memory it needs. This is pretty much universal in
|
||||
programming languages.
|
||||
|
||||
The second case, however, is different. In languages with a garbage collector
|
||||
(‘GC’), the GC handles that second case, and we, as the programmer, don’t need
|
||||
to think about it. Without GC, it’s the programmer’s responsibility to identify
|
||||
when memory is no longer being used, and explicitly return it, just as it was
|
||||
requested. Doing this correctly has historically been a difficult problem. If
|
||||
we forget, we will waste memory. If we do it too early, we will have an invalid
|
||||
variable. If we do it twice, that’s a bug too. We need to pair exactly one
|
||||
`allocate()` with exactly one `free()`.
|
||||
|
||||
Rust takes a different path. Remember our example? Here’s a version with
|
||||
`String`:
|
||||
|
||||
```rust
|
||||
{
|
||||
let s = String::from("hello"); // s is valid from this point forward
|
||||
|
||||
// do stuff with s
|
||||
} // this scope is now over, and s is no longer valid
|
||||
```
|
||||
|
||||
We have a natural point at which we can return the memory our `String` needs back
|
||||
to the operating system: when it goes out of scope! When a variable goes out of
|
||||
scope, a special function is called. This function is called `drop()`, and it
|
||||
is where the author of `String` can put the code to return the memory.
|
||||
|
||||
> Aside: This pattern is sometimes called “Resource Aquisition Is
|
||||
> Initialization” in C++, or “RAII” for short. While they are very similar,
|
||||
> Rust’s take on this concept has a number of differences, and so we don’t tend
|
||||
> to use the same term. If you’re familliar with this idea, keep in mind that it
|
||||
> is _roughly_ similar in Rust, but not identical.
|
||||
|
||||
This pattern has a profound impact on the way that Rust code is written. It may
|
||||
seem obvious right now, but things can get tricky in more advanced situations!
|
||||
Let’s go over the first one of those right now.
|
||||
|
||||
## Move
|
||||
|
||||
What would you expect this code to do?
|
||||
|
||||
```rust
|
||||
let x = 5;
|
||||
let y = x;
|
||||
```
|
||||
|
||||
You might say “Make a copy of `5`.” That’d be correct! We now have two
|
||||
bindings, `x` and `y`, and both equal `5`.
|
||||
|
||||
Now let’s look at `String`. What would you expect this code to do?
|
||||
|
||||
```rust
|
||||
let s1 = String::from("hello");
|
||||
let s2 = s1;
|
||||
```
|
||||
|
||||
You might say “copy the `String`!” This is both correct and incorrect at the
|
||||
same time. It does a _shallow_ copy of the `String`. What’s that mean? Well,
|
||||
let’s take a look at what `String` looks like under the covers:
|
||||
|
||||
<img alt="string" src="img/foo1.png" class="center" />
|
||||
|
||||
A `String` is made up of three parts: a pointer to the memory that holds the
|
||||
contents of the string, a length, and a capacity. The length is how much memory
|
||||
the `String` is currently using. The capacity is the total amount of memory the
|
||||
`String` has gotten from the operating system. The difference between length
|
||||
and capacity matters, but not in this context, so don’t worry about it too much
|
||||
if it doesn’t make sense, and just ignore the capacity.
|
||||
|
||||
> We’ve talked about two kinds of composite types: arrays and tuples. `String`
|
||||
> is a third type: a `struct`, which we will cover the details of in the next
|
||||
> chapter of the book. For now, thinking about `String` as a tuple is close
|
||||
> enough.
|
||||
|
||||
When we assign `s1` to `s2`, the `String` itself is copied. But not all kinds
|
||||
of copying are the same. Many people draw distinctions between ‘shallow
|
||||
copying’ and ‘deep copying’. We don’t use these terms in Rust. We instead say
|
||||
that something is ‘moved’ or ‘cloned’. Assignment in Rust causes a ‘move’. In
|
||||
other words, it looks like this:
|
||||
|
||||
<img alt="s1 and s2" src="img/foo2.png" class="center" />
|
||||
|
||||
_Not_ this:
|
||||
|
||||
<img alt="s1 and s2 to two places" src="img/foo4.png" class="center" />
|
||||
|
||||
When moving, Rust makes a copy of the data structure itself, the contents of
|
||||
`s1` are copied, but if `s1` contains a reference, like it does in this case,
|
||||
Rust will not copy the things that those references refer to.
|
||||
|
||||
There’s a problem here! Both `data` pointers are pointing to the same place.
|
||||
Why is this a problem? Well, when `s2` goes out of scope, it will free the
|
||||
memory that `data` points to. And then `s1` goes out of scope, and it will
|
||||
_also_ try to free the memory that `data` points to! That’s bad.
|
||||
|
||||
So what’s the solution? Here, we stand at a crossroads. There are a few
|
||||
options. One would be to declare that assignment will also copy out any data.
|
||||
This works, but is inefficient: what if our `String` contained a novel? Also,
|
||||
it only works for memory. What if, instead of a `String`, we had a
|
||||
`TcpConnection`? Opening and closing a network connection is very similar to
|
||||
allocating and freeing memory. The solution that we could use there is to allow
|
||||
the programmer to hook into the assignment, similar to `drop()`, and write code
|
||||
fix things up. That would work, but now, an `=` can run arbitrary code. That’s
|
||||
also not good, and it doesn’t solve our efficiency concerns either.
|
||||
|
||||
Let’s take a step back: the root of the problem is that `s1` and `s2` both
|
||||
think that they have control of the memory, and therefore needs to free it.
|
||||
Instead of trying to copy the allocated memory, we could say that `s1` is no
|
||||
longer valid, and therefore, doesn’t need to free anything. This is in fact the
|
||||
choice that Rust makes. Check it out what happens when you try to use `s1`
|
||||
after `s2` is created:
|
||||
|
||||
```rust,ignore
|
||||
let v = vec![1, 2, 3];
|
||||
let s1 = String::from("hello");
|
||||
let s2 = s1;
|
||||
|
||||
let v2 = v;
|
||||
|
||||
println!("v[0] is: {}", v[0]);
|
||||
println!("{}", s1);
|
||||
```
|
||||
|
||||
It looks like this:
|
||||
You’ll get an error like this:
|
||||
|
||||
```text
|
||||
error: use of moved value: `v`
|
||||
println!("v[0] is: {}", v[0]);
|
||||
^
|
||||
5:22 error: use of moved value: `s1` [E0382]
|
||||
println!("{}", s1);
|
||||
^~
|
||||
5:24 note: in this expansion of println! (defined in <std macros>)
|
||||
3:11 note: `s1` moved here because it has type `collections::string::String`, which is moved by default
|
||||
let s2 = s1;
|
||||
^~
|
||||
```
|
||||
|
||||
A similar thing happens if we define a function which takes ownership, and
|
||||
try to use something after we’ve passed it as an argument:
|
||||
We say that `s1` was _moved_ into `s2`. When a value moves, its data is copied,
|
||||
but the original variable binding is no longer usable. That solves our problem:
|
||||
|
||||
```rust,ignore
|
||||
fn take(v: Vec<i32>) {
|
||||
// what happens here isn’t important.
|
||||
}
|
||||
<img alt="s1 and s2 to the same place" src="img/foo3.png" class="center" />
|
||||
|
||||
let v = vec![1, 2, 3];
|
||||
With only `s2` valid, when it goes out of scope, it will free the memory, and we’re done!
|
||||
|
||||
take(v);
|
||||
## Ownership Rules
|
||||
|
||||
println!("v[0] is: {}", v[0]);
|
||||
```
|
||||
This leads us to the Ownership Rules:
|
||||
|
||||
Same error: ‘use of moved value’. When we transfer ownership to something else,
|
||||
we say that we’ve ‘moved’ the thing we refer to. You don’t need some sort of
|
||||
special annotation here, it’s the default thing that Rust does.
|
||||
> 1. Each value in Rust has a variable binding that’s called it’s ‘owner’.
|
||||
> 2. There can only be one owner at a time.
|
||||
> 3. When the owner goes out of scope, the value will be `drop()`ped.
|
||||
|
||||
## The details
|
||||
Furthermore, there’s a design choice that’s implied by this: Rust will never
|
||||
automatically create ‘deep’ copies of your data. Any automatic copying must be
|
||||
inexpensive.
|
||||
|
||||
The reason that we cannot use a binding after we’ve moved it is subtle, but
|
||||
important. When we write code like this:
|
||||
## Clone
|
||||
|
||||
But what if we _do_ want to deeply copy the `String`’s data, and not just the
|
||||
`String` itself? There’s a common method for that: `clone()`. Here’s an example
|
||||
of `clone()` in action:
|
||||
|
||||
```rust
|
||||
let v = vec![1, 2, 3];
|
||||
let s1 = String::from("hello");
|
||||
let s2 = s1.clone();
|
||||
|
||||
let v2 = v;
|
||||
println!("{}", s1);
|
||||
```
|
||||
|
||||
The first line allocates memory for the vector object, `v`, and for the data it
|
||||
contains. The vector object is stored on the [stack][sh] and contains a pointer
|
||||
to the content (`[1, 2, 3]`) stored on the [heap][sh]. When we move `v` to `v2`,
|
||||
it creates a copy of that pointer, for `v2`. Which means that there would be two
|
||||
pointers to the content of the vector on the heap. It would violate Rust’s
|
||||
safety guarantees by introducing a data race. Therefore, Rust forbids using `v`
|
||||
after we’ve done the move.
|
||||
This will work just fine. Remember our diagram from before? In this case,
|
||||
it _is_ doing this:
|
||||
|
||||
[sh]: the-stack-and-the-heap.html
|
||||
<img alt="s1 and s2 to two places" src="img/foo4.png" class="center" />
|
||||
|
||||
It’s also important to note that optimizations may remove the actual copy of
|
||||
the bytes on the stack, depending on circumstances. So it may not be as
|
||||
inefficient as it initially seems.
|
||||
When you see a call to `clone()`, you know that some arbitrary code is being
|
||||
executed, which may be expensive. It’s a visual indicator that something
|
||||
different is going on here.
|
||||
|
||||
## `Copy` types
|
||||
## Copy
|
||||
|
||||
We’ve established that when ownership is transferred to another binding, you
|
||||
cannot use the original binding. However, there’s a [trait][traits] that changes this
|
||||
behavior, and it’s called `Copy`. We haven’t discussed traits yet, but for now,
|
||||
you can think of them as an annotation to a particular type that adds extra
|
||||
behavior. For example:
|
||||
There’s one last wrinkle that we haven’t talked about yet. This code works:
|
||||
|
||||
```rust
|
||||
let v = 1;
|
||||
let x = 5;
|
||||
let y = x;
|
||||
|
||||
let v2 = v;
|
||||
|
||||
println!("v is: {}", v);
|
||||
println!("{}", x);
|
||||
```
|
||||
|
||||
In this case, `v` is an `i32`, which implements the `Copy` trait. This means
|
||||
that, just like a move, when we assign `v` to `v2`, a copy of the data is made.
|
||||
But, unlike a move, we can still use `v` afterward. This is because an `i32`
|
||||
has no pointers to data somewhere else, copying it is a full copy.
|
||||
But why? We don’t have a call to `clone()`. Why didn’t `x` get moved into `y`?
|
||||
|
||||
All primitive types implement the `Copy` trait and their ownership is
|
||||
therefore not moved like one would assume, following the ´ownership rules´.
|
||||
To give an example, the two following snippets of code only compile because the
|
||||
`i32` and `bool` types implement the `Copy` trait.
|
||||
For types that do not have any kind of complex storage requirements, like
|
||||
integers, typing `clone()` is busy work. There’s no reason we would ever want
|
||||
to prevent `x` from being valid here, as there’s no situation in which it’s
|
||||
incorrect. In other words, there’s no difference between deep and shallow
|
||||
copying here, so calling `clone()` wouldn’t do anything differently from the
|
||||
usual shallow copying.
|
||||
|
||||
Rust has a special annotation that you can place on types, called `Copy`. If
|
||||
a type is `Copy`, an older binding is still usable after assignment. Integers
|
||||
are an example of such a type; most of the primitive types are `Copy`.
|
||||
|
||||
While we haven’t talked about how to mark a type as `Copy` yet, you might ask
|
||||
yourself “what happens if we made `String` `Copy`?” The answer is, you cannot.
|
||||
Remember `drop()`? Rust will not let you make something `Copy` if it has
|
||||
implemented `drop()`. If you need to do something special when the value goes
|
||||
out of scope, being `Copy` will be an error.
|
||||
|
||||
So what types are `Copy`? You can check the documentation for the given type to
|
||||
be sure, but as a rule of thumb, any group of simple scalar values can be
|
||||
Copy, but nothing that requires allocation or is some form of resource is `Copy`.
|
||||
And you can’t get it wrong: the compiler will throw an error if you try to use
|
||||
a type that moves incorrectly, as we saw above.
|
||||
|
||||
Here’s some types that you’ve seen so far that are `Copy`:
|
||||
|
||||
* All of the integer types, like `u32`.
|
||||
* The booleans, `true` and `false`.
|
||||
* All of the floating point types, like `f64`.
|
||||
* Tuples, but only if they contain types which are also `Copy`. `(i32, i32)`
|
||||
is `Copy`, but `(i32, String)` is not!
|
||||
|
||||
## Ownership and functions
|
||||
|
||||
Passing a value to a function has similar semantics as assigning it:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let a = 5;
|
||||
let s = String::from("hello");
|
||||
|
||||
let _y = double(a);
|
||||
println!("{}", a);
|
||||
takes_ownership(s);
|
||||
|
||||
let x = 5;
|
||||
|
||||
makes_copy(x);
|
||||
}
|
||||
|
||||
fn double(x: i32) -> i32 {
|
||||
x * 2
|
||||
fn takes_ownership(some_string: String) {
|
||||
println!("{}", some_string);
|
||||
}
|
||||
|
||||
fn makes_copy(some_integer: i32) {
|
||||
println!("{}", some_integer);
|
||||
}
|
||||
```
|
||||
|
||||
Passing a binding to a function will move or copy, just like assignment. Here’s
|
||||
the same example, but with some annotations showing where things go into and
|
||||
out of scope:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let a = true;
|
||||
let s = String::from("hello"); // s goes into scope.
|
||||
|
||||
let _y = change_truth(a);
|
||||
println!("{}", a);
|
||||
}
|
||||
takes_ownership(s); // s moves into the function...
|
||||
// ... and so is no longer valid here.
|
||||
let x = 5; // x goes into scope.
|
||||
|
||||
fn change_truth(x: bool) -> bool {
|
||||
!x
|
||||
}
|
||||
makes_copy(x); // x would move into the function,
|
||||
// but i32 is Copy, so it’s okay to still
|
||||
// use x afterward.
|
||||
|
||||
} // Here, x goes out of scope, then s. But since s was moved, nothing special
|
||||
// happens.
|
||||
|
||||
fn takes_ownership(some_string: String) { // some_string comes into scope.
|
||||
println!("{}", some_string);
|
||||
} // Here, some_string goes out of scope and `drop()` is called. The backing
|
||||
// memory is freed.
|
||||
|
||||
fn makes_copy(some_integer: i32) { // some_integer comes into scope.
|
||||
println!("{}", some_integer);
|
||||
} // Here, some_integer goes out of scope. Nothing special happens.
|
||||
```
|
||||
|
||||
If we had used types that do not implement the `Copy` trait,
|
||||
we would have gotten a compile error because we tried to use a moved value.
|
||||
Remember: If we tried to use `s` after the call to `takes_ownership()`, Rust
|
||||
would throw a compile-time error! These static checks protect us from mistakes.
|
||||
|
||||
```text
|
||||
error: use of moved value: `a`
|
||||
println!("{}", a);
|
||||
^
|
||||
```
|
||||
|
||||
We will discuss how to make your own types `Copy` in the [traits][traits]
|
||||
section.
|
||||
|
||||
[traits]: traits.html
|
||||
|
||||
# More than ownership
|
||||
|
||||
Of course, if we had to hand ownership back with every function we wrote:
|
||||
Returning values can also transfer ownership:
|
||||
|
||||
```rust
|
||||
fn foo(v: Vec<i32>) -> Vec<i32> {
|
||||
// do stuff with v
|
||||
fn main() {
|
||||
let s1 = gives_ownership();
|
||||
|
||||
// hand back ownership
|
||||
v
|
||||
let s2 = String::from("hello");
|
||||
|
||||
let s3 = takes_and_gives_back(s2);
|
||||
}
|
||||
|
||||
fn gives_ownership() -> String {
|
||||
let some_string = String::from("hello");
|
||||
|
||||
some_string
|
||||
}
|
||||
|
||||
fn takes_and_gives_back(a_string: String) -> String {
|
||||
|
||||
a_string
|
||||
}
|
||||
```
|
||||
|
||||
This would get very tedious. It gets worse the more things we want to take ownership of:
|
||||
With simililar annotations:
|
||||
|
||||
```rust
|
||||
fn foo(v1: Vec<i32>, v2: Vec<i32>) -> (Vec<i32>, Vec<i32>, i32) {
|
||||
// do stuff with v1 and v2
|
||||
fn main() {
|
||||
let s1 = gives_ownership(); // gives_ownership moves its return
|
||||
// value into s1.
|
||||
|
||||
// hand back ownership, and the result of our function
|
||||
(v1, v2, 42)
|
||||
let s2 = String::from("hello"); // s2 comes into scope
|
||||
|
||||
let s3 = takes_and_gives_back(s2); // s2 is moved into
|
||||
// takes_and_gives_back, which also
|
||||
// moves its return value into s3.
|
||||
} // Here, s3 goes out of scope, and is dropped. s2 goes out of scope, but was
|
||||
// moved, so nothing happens. s1 goes out of scope, and is dropped.
|
||||
|
||||
fn gives_ownership() -> String { // gives_ownership will move its
|
||||
// return value into the function
|
||||
// that calls it.
|
||||
|
||||
let some_string = String::from("hello"); // some_string comes into scope.
|
||||
|
||||
some_string // some_string is returned, and
|
||||
// moves out to the calling
|
||||
// function.
|
||||
}
|
||||
|
||||
let v1 = vec![1, 2, 3];
|
||||
let v2 = vec![1, 2, 3];
|
||||
// takes_and_gives_back will both take a String and return one
|
||||
fn takes_and_gives_back(a_string: String) -> String { // a_string comes into scope
|
||||
|
||||
let (v1, v2, answer) = foo(v1, v2);
|
||||
a_string // a_string is returned, and moves out to the calling function
|
||||
}
|
||||
```
|
||||
|
||||
Ugh! The return type, return line, and calling the function gets way more
|
||||
complicated.
|
||||
It’s the same pattern, every time: assigning something moves it, and when an
|
||||
owner goes out of scope, if it hasn’t been moved, it will `drop()`.
|
||||
|
||||
Luckily, Rust offers a feature, borrowing, which helps us solve this problem.
|
||||
It’s the topic of the next section!
|
||||
This might seem a bit tedious, and it is. What if I want to let a function use
|
||||
a value, but not take ownership? It’s quite annoying that anything I pass in
|
||||
also needs passed back. Look at this function:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let s1 = String::from("hello");
|
||||
|
||||
let (s2, len) = calculate_length(s1);
|
||||
|
||||
println!("The length of '{}' is {}.", s2, len);
|
||||
}
|
||||
|
||||
fn calculate_length(s: String) -> (String, usize) {
|
||||
let length = s.len(); // len() returns the length of a String.
|
||||
|
||||
(s, length)
|
||||
}
|
||||
```
|
||||
|
||||
This is too much ceremony: we have to use a tuple to give back the `String` as
|
||||
well as the length. It’s a lot of work for a pattern that should be common.
|
||||
|
||||
Luckily for us, Rust has such a feature, and it’s what the next section is about.
|
||||
|
587
src/theme/book.css
Normal file
587
src/theme/book.css
Normal file
@ -0,0 +1,587 @@
|
||||
html,
|
||||
body {
|
||||
font-family: "Open Sans", sans-serif;
|
||||
color: #333;
|
||||
}
|
||||
.left {
|
||||
float: left;
|
||||
}
|
||||
.right {
|
||||
float: right;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
h2,
|
||||
h3 {
|
||||
margin-top: 2.5em;
|
||||
}
|
||||
h4,
|
||||
h5 {
|
||||
margin-top: 2em;
|
||||
}
|
||||
.header + .header h3,
|
||||
.header + .header h4,
|
||||
.header + .header h5 {
|
||||
margin-top: 1em;
|
||||
}
|
||||
.sidebar {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 300px;
|
||||
overflow-y: auto;
|
||||
padding: 10px 10px;
|
||||
font-size: 0.875em;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
-webkit-transition: left 0.5s;
|
||||
-moz-transition: left 0.5s;
|
||||
-o-transition: left 0.5s;
|
||||
-ms-transition: left 0.5s;
|
||||
transition: left 0.5s;
|
||||
}
|
||||
@media only screen and (max-width: 1060px) {
|
||||
.sidebar {
|
||||
left: -300px;
|
||||
}
|
||||
}
|
||||
.sidebar code {
|
||||
line-height: 2em;
|
||||
}
|
||||
.sidebar-hidden .sidebar {
|
||||
left: -300px;
|
||||
}
|
||||
.sidebar-visible .sidebar {
|
||||
left: 0;
|
||||
}
|
||||
.chapter {
|
||||
list-style: none outside none;
|
||||
padding-left: 0;
|
||||
line-height: 1.9em;
|
||||
}
|
||||
.chapter li a {
|
||||
padding: 5px 0;
|
||||
text-decoration: none;
|
||||
}
|
||||
.chapter li a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.chapter .spacer {
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
margin: 10px 0px;
|
||||
}
|
||||
.section {
|
||||
list-style: none outside none;
|
||||
padding-left: 20px;
|
||||
line-height: 2.5em;
|
||||
}
|
||||
.section li {
|
||||
-o-text-overflow: ellipsis;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.page-wrapper {
|
||||
position: absolute;
|
||||
overflow-y: auto;
|
||||
left: 315px;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
min-height: 100%;
|
||||
-webkit-transition: left 0.5s;
|
||||
-moz-transition: left 0.5s;
|
||||
-o-transition: left 0.5s;
|
||||
-ms-transition: left 0.5s;
|
||||
transition: left 0.5s;
|
||||
}
|
||||
@media only screen and (max-width: 1060px) {
|
||||
.page-wrapper {
|
||||
left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
}
|
||||
.sidebar-hidden .page-wrapper {
|
||||
left: 15px;
|
||||
}
|
||||
.sidebar-visible .page-wrapper {
|
||||
left: 315px;
|
||||
}
|
||||
.page {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
padding-right: 15px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.content {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 750px;
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
.content a {
|
||||
text-decoration: none;
|
||||
}
|
||||
.content a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.content img {
|
||||
max-width: 100%;
|
||||
}
|
||||
.menu-bar {
|
||||
position: relative;
|
||||
height: 50px;
|
||||
}
|
||||
.menu-bar i {
|
||||
position: relative;
|
||||
margin: 0 10px;
|
||||
z-index: 10;
|
||||
line-height: 50px;
|
||||
-webkit-transition: color 0.5s;
|
||||
-moz-transition: color 0.5s;
|
||||
-o-transition: color 0.5s;
|
||||
-ms-transition: color 0.5s;
|
||||
transition: color 0.5s;
|
||||
}
|
||||
.menu-bar i:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
.menu-bar .left-buttons {
|
||||
float: left;
|
||||
}
|
||||
.menu-bar .right-buttons {
|
||||
float: right;
|
||||
}
|
||||
.menu-title {
|
||||
display: inline-block;
|
||||
font-weight: 200;
|
||||
font-size: 20px;
|
||||
line-height: 50px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
opacity: 0;
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
|
||||
filter: alpha(opacity=0);
|
||||
-webkit-transition: opacity 0.5s ease-in-out;
|
||||
-moz-transition: opacity 0.5s ease-in-out;
|
||||
-o-transition: opacity 0.5s ease-in-out;
|
||||
-ms-transition: opacity 0.5s ease-in-out;
|
||||
transition: opacity 0.5s ease-in-out;
|
||||
}
|
||||
.menu-bar:hover .menu-title {
|
||||
opacity: 1;
|
||||
-ms-filter: none;
|
||||
filter: none;
|
||||
}
|
||||
.nav-chapters {
|
||||
font-size: 2.5em;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
position: absolute;
|
||||
top: 50px /* Height of menu-bar */;
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
max-width: 150px;
|
||||
min-width: 90px;
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: box;
|
||||
display: flex;
|
||||
-webkit-box-pack: center;
|
||||
-moz-box-pack: center;
|
||||
-o-box-pack: center;
|
||||
-ms-flex-pack: center;
|
||||
-webkit-justify-content: center;
|
||||
justify-content: center;
|
||||
-ms-flex-line-pack: center;
|
||||
-webkit-align-content: center;
|
||||
align-content: center;
|
||||
-webkit-box-orient: vertical;
|
||||
-moz-box-orient: vertical;
|
||||
-o-box-orient: vertical;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
-webkit-transition: color 0.5s;
|
||||
-moz-transition: color 0.5s;
|
||||
-o-transition: color 0.5s;
|
||||
-ms-transition: color 0.5s;
|
||||
transition: color 0.5s;
|
||||
}
|
||||
.mobile-nav-chapters {
|
||||
display: none;
|
||||
}
|
||||
.nav-chapters:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.previous {
|
||||
left: 0;
|
||||
}
|
||||
.next {
|
||||
right: 15px;
|
||||
}
|
||||
.theme-popup {
|
||||
position: fixed;
|
||||
left: -40px;
|
||||
-webkit-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.7em;
|
||||
}
|
||||
.theme-popup .theme {
|
||||
margin: 0;
|
||||
padding: 2px 10px;
|
||||
line-height: 25px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
@media only screen and (max-width: 1250px) {
|
||||
.nav-chapters {
|
||||
display: none;
|
||||
}
|
||||
.mobile-nav-chapters {
|
||||
font-size: 2.5em;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
max-width: 150px;
|
||||
min-width: 90px;
|
||||
-webkit-box-pack: center;
|
||||
-moz-box-pack: center;
|
||||
-o-box-pack: center;
|
||||
-ms-flex-pack: center;
|
||||
-webkit-justify-content: center;
|
||||
justify-content: center;
|
||||
-ms-flex-line-pack: center;
|
||||
-webkit-align-content: center;
|
||||
align-content: center;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin-bottom: 50px;
|
||||
-webkit-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.next {
|
||||
float: right;
|
||||
}
|
||||
.previous {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
.light {
|
||||
/* Inline code */
|
||||
color: #333;
|
||||
background-color: #fff;
|
||||
}
|
||||
.light :not(pre) > .hljs {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
padding: 0.1em 0.3em;
|
||||
-webkit-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.light pre {
|
||||
position: relative;
|
||||
}
|
||||
.light pre > i {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
color: #364149;
|
||||
cursor: pointer;
|
||||
}
|
||||
.light pre > i :hover {
|
||||
color: #008cff;
|
||||
}
|
||||
.light .sidebar {
|
||||
background-color: #fafafa;
|
||||
color: #364149;
|
||||
}
|
||||
.light .chapter li {
|
||||
color: #aaa;
|
||||
}
|
||||
.light .chapter li a {
|
||||
color: #364149;
|
||||
}
|
||||
.light .chapter li .active,
|
||||
.light .chapter li a:hover {
|
||||
/* Animate color change */
|
||||
color: #008cff;
|
||||
}
|
||||
.light .chapter .spacer {
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
.light .menu-bar,
|
||||
.light .menu-bar:visited,
|
||||
.light .nav-chapters,
|
||||
.light .nav-chapters:visited,
|
||||
.light .mobile-nav-chapters,
|
||||
.light .mobile-nav-chapters:visited {
|
||||
color: #ccc;
|
||||
}
|
||||
.light .menu-bar i:hover,
|
||||
.light .nav-chapters:hover,
|
||||
.light .mobile-nav-chapters i:hover {
|
||||
color: #333;
|
||||
}
|
||||
.light .mobile-nav-chapters i:hover {
|
||||
color: #364149;
|
||||
}
|
||||
.light .mobile-nav-chapters {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
.light .content a:link,
|
||||
.light a:visited {
|
||||
color: #4183c4;
|
||||
}
|
||||
.light .theme-popup {
|
||||
background: #fafafa;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
.light .theme-popup .theme:hover {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
.coal {
|
||||
/* Inline code */
|
||||
color: #98a3ad;
|
||||
background-color: #141617;
|
||||
}
|
||||
.coal :not(pre) > .hljs {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
padding: 0.1em 0.3em;
|
||||
-webkit-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.coal pre {
|
||||
position: relative;
|
||||
}
|
||||
.coal pre > i {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
color: #a1adb8;
|
||||
cursor: pointer;
|
||||
}
|
||||
.coal pre > i :hover {
|
||||
color: #3473ad;
|
||||
}
|
||||
.coal .sidebar {
|
||||
background-color: #292c2f;
|
||||
color: #a1adb8;
|
||||
}
|
||||
.coal .chapter li {
|
||||
color: #505254;
|
||||
}
|
||||
.coal .chapter li a {
|
||||
color: #a1adb8;
|
||||
}
|
||||
.coal .chapter li .active,
|
||||
.coal .chapter li a:hover {
|
||||
/* Animate color change */
|
||||
color: #3473ad;
|
||||
}
|
||||
.coal .chapter .spacer {
|
||||
background-color: #393939;
|
||||
}
|
||||
.coal .menu-bar,
|
||||
.coal .menu-bar:visited,
|
||||
.coal .nav-chapters,
|
||||
.coal .nav-chapters:visited,
|
||||
.coal .mobile-nav-chapters,
|
||||
.coal .mobile-nav-chapters:visited {
|
||||
color: #43484d;
|
||||
}
|
||||
.coal .menu-bar i:hover,
|
||||
.coal .nav-chapters:hover,
|
||||
.coal .mobile-nav-chapters i:hover {
|
||||
color: #b3c0cc;
|
||||
}
|
||||
.coal .mobile-nav-chapters i:hover {
|
||||
color: #a1adb8;
|
||||
}
|
||||
.coal .mobile-nav-chapters {
|
||||
background-color: #292c2f;
|
||||
}
|
||||
.coal .content a:link,
|
||||
.coal a:visited {
|
||||
color: #2b79a2;
|
||||
}
|
||||
.coal .theme-popup {
|
||||
background: #141617;
|
||||
border: 1px solid #43484d;
|
||||
}
|
||||
.coal .theme-popup .theme:hover {
|
||||
background-color: #1f2124;
|
||||
}
|
||||
.navy {
|
||||
/* Inline code */
|
||||
color: #bcbdd0;
|
||||
background-color: #161923;
|
||||
}
|
||||
.navy :not(pre) > .hljs {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
padding: 0.1em 0.3em;
|
||||
-webkit-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.navy pre {
|
||||
position: relative;
|
||||
}
|
||||
.navy pre > i {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
color: #c8c9db;
|
||||
cursor: pointer;
|
||||
}
|
||||
.navy pre > i :hover {
|
||||
color: #2b79a2;
|
||||
}
|
||||
.navy .sidebar {
|
||||
background-color: #282d3f;
|
||||
color: #c8c9db;
|
||||
}
|
||||
.navy .chapter li {
|
||||
color: #505274;
|
||||
}
|
||||
.navy .chapter li a {
|
||||
color: #c8c9db;
|
||||
}
|
||||
.navy .chapter li .active,
|
||||
.navy .chapter li a:hover {
|
||||
/* Animate color change */
|
||||
color: #2b79a2;
|
||||
}
|
||||
.navy .chapter .spacer {
|
||||
background-color: #2d334f;
|
||||
}
|
||||
.navy .menu-bar,
|
||||
.navy .menu-bar:visited,
|
||||
.navy .nav-chapters,
|
||||
.navy .nav-chapters:visited,
|
||||
.navy .mobile-nav-chapters,
|
||||
.navy .mobile-nav-chapters:visited {
|
||||
color: #737480;
|
||||
}
|
||||
.navy .menu-bar i:hover,
|
||||
.navy .nav-chapters:hover,
|
||||
.navy .mobile-nav-chapters i:hover {
|
||||
color: #b7b9cc;
|
||||
}
|
||||
.navy .mobile-nav-chapters i:hover {
|
||||
color: #c8c9db;
|
||||
}
|
||||
.navy .mobile-nav-chapters {
|
||||
background-color: #282d3f;
|
||||
}
|
||||
.navy .content a:link,
|
||||
.navy a:visited {
|
||||
color: #2b79a2;
|
||||
}
|
||||
.navy .theme-popup {
|
||||
background: #161923;
|
||||
border: 1px solid #737480;
|
||||
}
|
||||
.navy .theme-popup .theme:hover {
|
||||
background-color: #282e40;
|
||||
}
|
||||
.rust {
|
||||
/* Inline code */
|
||||
color: #262625;
|
||||
background-color: #e1e1db;
|
||||
}
|
||||
.rust :not(pre) > .hljs {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
padding: 0.1em 0.3em;
|
||||
-webkit-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.rust pre {
|
||||
position: relative;
|
||||
}
|
||||
.rust pre > i {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
color: #c8c9db;
|
||||
cursor: pointer;
|
||||
}
|
||||
.rust pre > i :hover {
|
||||
color: #e69f67;
|
||||
}
|
||||
.rust .sidebar {
|
||||
background-color: #3b2e2a;
|
||||
color: #c8c9db;
|
||||
}
|
||||
.rust .chapter li {
|
||||
color: #505254;
|
||||
}
|
||||
.rust .chapter li a {
|
||||
color: #c8c9db;
|
||||
}
|
||||
.rust .chapter li .active,
|
||||
.rust .chapter li a:hover {
|
||||
/* Animate color change */
|
||||
color: #e69f67;
|
||||
}
|
||||
.rust .chapter .spacer {
|
||||
background-color: #45373a;
|
||||
}
|
||||
.rust .menu-bar,
|
||||
.rust .menu-bar:visited,
|
||||
.rust .nav-chapters,
|
||||
.rust .nav-chapters:visited,
|
||||
.rust .mobile-nav-chapters,
|
||||
.rust .mobile-nav-chapters:visited {
|
||||
color: #737480;
|
||||
}
|
||||
.rust .menu-bar i:hover,
|
||||
.rust .nav-chapters:hover,
|
||||
.rust .mobile-nav-chapters i:hover {
|
||||
color: #262625;
|
||||
}
|
||||
.rust .mobile-nav-chapters i:hover {
|
||||
color: #c8c9db;
|
||||
}
|
||||
.rust .mobile-nav-chapters {
|
||||
background-color: #3b2e2a;
|
||||
}
|
||||
.rust .content a:link,
|
||||
.rust a:visited {
|
||||
color: #2b79a2;
|
||||
}
|
||||
.rust .theme-popup {
|
||||
background: #e1e1db;
|
||||
border: 1px solid #b38f6b;
|
||||
}
|
||||
.rust .theme-popup .theme:hover {
|
||||
background-color: #99908a;
|
||||
}
|
||||
|
||||
/* Everything above is the default theme, below is custom */
|
||||
.center {
|
||||
text-align: center;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user