Skip to content
Snippets Groups Projects
Commit c0a10e9c authored by Jasper Clemens Gräflich's avatar Jasper Clemens Gräflich
Browse files

Begin subsection 1.1.2 Borrowing

parent 5e484516
No related branches found
No related tags found
No related merge requests found
Pipeline #86549 passed
...@@ -90,13 +90,13 @@ associated resources are freed. It is said that a variable has a \emph{drop resp ...@@ -90,13 +90,13 @@ associated resources are freed. It is said that a variable has a \emph{drop resp
\ac{RAII} comes with a few disadvantages, though. Since a value is dropped when its owner goes out of scope, ther must always be exactly one owner for every value. This means that the compiler will transfer ownership sometimes, like in the following example. \ac{RAII} comes with a few disadvantages, though. Since a value is dropped when its owner goes out of scope, ther must always be exactly one owner for every value. This means that the compiler will transfer ownership sometimes, like in the following example.
\begin{lstlisting}[language=Rust,caption={Ownership transfer}] \begin{lstlisting}[language=Rust,caption={Ownership transfer},label={lst:ownership-transfer}]
fn print_value(ptr: Box<i32>) { fn print_value(ptr: Box<i32>) {
println!("{}", *ptr); println!("{}", *ptr);
} }
fn main() { fn main() {
let ptr = Box::new(42); let mut ptr = Box::new(42);
print_value(ptr); print_value(ptr);
...@@ -124,7 +124,6 @@ fn print_value(ptr: Box<i32>) { ...@@ -124,7 +124,6 @@ fn print_value(ptr: Box<i32>) {
fn main() { fn main() {
let ptr = Box::new(42); let ptr = Box::new(42);
// v
print_value(ptr.clone()); print_value(ptr.clone());
*ptr += 1; *ptr += 1;
...@@ -143,7 +142,7 @@ fn print_value(number: i32) { ...@@ -143,7 +142,7 @@ fn print_value(number: i32) {
} }
fn main() { fn main() {
let number = 42; let mut number = 42;
print_value(number); print_value(number);
...@@ -153,10 +152,38 @@ fn main() { ...@@ -153,10 +152,38 @@ fn main() {
This compiles and works. That is not because \lstinline{i32} breaks move semantics but because the type implements the \lstinline{Copy} trait. Normally, if a value is moved, the physical bits making up that value are moved to a new location, e. g. the new stack frame, and are not available at the previous position anymore. If a type implements \lstinline{Copy}, the bit pattern is instead copied over and retained. Because \lstinline{i32} doesn’t allocate any heap or handles any resources, such a copy is valid. A \lstinline{Box} is not \lstinline{Copy}, because then two owners for the same resource would exist which would violate drop responsibility. If we want to duplicate a \lstinline{Box}, we need to allocate new memory on the heap and initialize it properly, which is what \lstinline{clone} does. This compiles and works. That is not because \lstinline{i32} breaks move semantics but because the type implements the \lstinline{Copy} trait. Normally, if a value is moved, the physical bits making up that value are moved to a new location, e. g. the new stack frame, and are not available at the previous position anymore. If a type implements \lstinline{Copy}, the bit pattern is instead copied over and retained. Because \lstinline{i32} doesn’t allocate any heap or handles any resources, such a copy is valid. A \lstinline{Box} is not \lstinline{Copy}, because then two owners for the same resource would exist which would violate drop responsibility. If we want to duplicate a \lstinline{Box}, we need to allocate new memory on the heap and initialize it properly, which is what \lstinline{clone} does.
\paragraph{Manually Drop} Sometimes we don’t want \ac{RAII} to happen, we want to free resources ourselves. If that is the case, we can wrap a value in a \lstinline{ManuallyDrop} which tells the compiler to not drop it for us. More information can be found in the documentation for the type.\footnote{\url{https://doc.rust-lang.org/stable/std/mem/struct.ManuallyDrop.html}} \paragraph{Manually Drop} Sometimes we don’t want \ac{RAII} to happen, we want to free resources ourselves. If that is the case, we can wrap a value in a \lstinline{ManuallyDrop} which tells the compiler to not drop it for us. This is a general trend in Rust: The correct way should be the easiest to do, and all potentially unsafe constructs are opt-in. More information on \lstinline{ManuallyDrop} can be found in the documentation for the type.\footnote{\url{https://doc.rust-lang.org/stable/std/mem/struct.ManuallyDrop.html}}
\subsection{Borrowing} \subsection{Borrowing}
As we have seen, ownership and move semantics ensure memory safety. But they are not easy to use, \lstinline{clone}ing data has a runtime overhead, and many correct programs are rejected. For example, \autoref{lst:ownership-transfer} describes a program that would work if it were not for move semantics.
That is where references and borrowing come in. Instead of taking ownership of a value, a function can only borrow it through a reference. Then the drop responsibility stays with the caller. References, of course, can not be used for everything, but for our case it is sufficcient. We mark the argument to \lstinline{print_value} as a reference using \lstinline{&}, and creating a reference from a value works the same.
\begin{lstlisting}[language=Rust, caption={Borrow through references to prevent a move}, label={lst:borrow}]
fn print_value(ptr: &Box<i32>) {
println!("{}", *ptr);
}
fn main() {
let mut ptr = Box::new(42);
print_value(&ptr);
*ptr += 1;
}
\end{lstlisting}
\autoref{lst:borrow} compiles without an error and does what we would expect. There are two kinds of references. We just looked at \emph{shared} or \emph{immutable references}. The other kind is \emph{unique} or \emph{mutable references}, and they are denoted with \lstinline[language=Rust]{&mut}. The different kinds of references have a different semantics attached to them. While both are used to access values without taking ownership, there are specific rules for the creation and guarantees associated with each:
\begin{itemize}
\item A shared reference to a value can always be created, as long as there is no unique reference to the same value. It is not possible to mutate the pontee through a shared reference (which is why it is also called immutable).
\item A unique reference can only be created if there is no other reference to the value at all and the referenced value is declared mutable. A unique reference can do everything the owner can, except dropping.
\end{itemize}
The part of the compiler that enforces these rules is the \emph{borrow checker}. The reasoning behind this is memory safety.
\begin{enumerate} \begin{enumerate}
\item unguarded aliasing + mutability = race conditions \item unguarded aliasing + mutability = race conditions
\item The borrow checker prevents this \item The borrow checker prevents this
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment