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

Continue 1.1.2 Borrowing

parent 5fa45330
No related branches found
No related tags found
No related merge requests found
Pipeline #86550 passed
......@@ -152,7 +152,7 @@ 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.
\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}}
\paragraph{ManuallyDrop} 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}
......@@ -182,16 +182,50 @@ fn main() {
\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.
The part of the compiler that enforces these rules is the \emph{borrow checker}. The reasoning behind this is again resource safety, namely preventing \emph{unguarded mutable aliasing}. Having multiple readers of the same data doesn’t cause issues, as long as the data cannot be mutated. Mutating data is fine, as long as no one else can read and/or mutate the data at the same time. Using these two kinds of references enforces, at compile time, that we will always stay on the happy path. But there are some programs that are correct even though they violate these rules. We will be concerned with extending the borrow checker to accept more correct programs in later sections.
\paragraph{The Owner of a Borrowed Value}
References have to agree to these rules, but the owner has to as well. While shared references exist, the owner may not mutate, e. g. \lstinline{let mut x = 42; let xref = &x; x += 1} is forbidden. Similarly, the owner can’t access a value at all as long as there is a unique reference around.
\todo[inline]{
\textbf{This doesn’t not work, I have to think about this some more. I want something to throe Error E0507.}
In particular, a value may not be moved, as long as there is a reference to it because that would create a dangling pointer. This means that \lstinline{let y = &x; let z = *y;} doesn’t work.
%\begin{lstlisting}[language=Rust, caption={Moving out of borrowed value}]
% let x = 42;
% let xref = &x;
% let y = x;
%\end{lstlisting}
}
\paragraph{Lexical Lifetimes}
Before we can discuss extensions to the borrow checker, let’s discuss naïve references. References are values like every other and therefore have an owner themselves. That means if we create a reference to a value, the owner can only access it, after the reference goes out of scope.
\begin{lstlisting}[language=Rust, caption={Lexical Lifetimes Prevent Usage}, label={lst:lexical-lifetimes}]
let mut x = 0;
{
let xref = &x; // Create a reference to `x`
x += 1; // Error: Can’t mutate while a reference exists
}
x += 1; // OK, no references exist anymore
\end{lstlisting}
The program in \autoref{lst:lexical-lifetimes} is rejected by our naïve borrow checker since it registers a mutation to \lstinline{x} while a reference to it still exists. Only after the block ends, the borrow is returned to the owner and it can use it freely again. Similarly, we would not be able to create a unique reference while a shared one exists and vice versa. But we can see that the program is correct since \lstinline{xref} is never accessed and therefore no aliasing happens. This code could be saved by wrapping the line in an extra pair of braces but we would like to avoid it. This can be achieved with \acfi{NLL}, which are part of Rust since version 1.31.0.
\paragraph{Access Guards}\todo{Put this in an info box?}
Sometimes we need access to a resource from multiple places at the same time, for example when sharing data between threads. For this, Rust provides the \lstinline{Mutex} container type. References to a mutex can be shared freely, but to change the value in the container, one has to acquire a lock, therefore making the access \emph{guarded}. While the mutex is locked, no other thread can access the data at all, a mutex lock is therefore similar to a \lstinline[language=Rust]{&mut} but its guarantees are enforced at runtime. The \lstinline{RwLock} type can give out both read and write locks which have behavior analogous to \lstinline[language=Rust]{&} and \lstinline[language=Rust]{&mut}, respectively. There are more constructs for similar use cases in the standard library, like \lstinline{Arc} and \lstinline{Cow}.
These data structures are implemented using so-called \emph{raw pointers}. Raw pointers, like pointers in C, are not borrow checked and can therefore alias. The programmer has to check manually that no rules are violated and should provide a safe interface that hides the raw pointers from the users. \todo{Links to Strict Provenance, Miri, …}
\begin{enumerate}
\item unguarded aliasing + mutability = race conditions
\item The borrow checker prevents this
\item two kinds of references
\item returning a borrow to the owner (lexical lifetimes)
\item moving out of a borrowed value is illegal (e. g. \lstinline{let y = &x; let z = *y;})
\item lifetime polymorphism and lifetime elision
\todo[inline]{borrow-through}
\todo[inline]{borrow-through}
\end{enumerate}
\subsection{Non-Lexical Lifetimes}
\begin{enumerate}
......@@ -207,7 +241,7 @@ The part of the compiler that enforces these rules is the \emph{borrow checker}.
\item Example
\item Solution: Two-Phase Borrowing
\item Note: TPB is not yet implemented
\todo[inline]{Fit in the Borrow Stack}
\todo[inline]{Fit in the Borrow Stack}
\end{enumerate}
\subsection{Loans and Regions}
\begin{enumerate}
......
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