This manual resource management comes with some difficulties. The programmer has to watch out to always free the resources after using them, because otherwise the program could leak memory or unneccessarily inhibit other processes from accessing the resources. On the other hand, one may also not free the resources too early, which would lead to a use after free, and also not free resources multiple times. This can be very tricky in complex programs where resources are shared between threads or over \acs{API} boundaries.
This manual resource management comes with some difficulties. The programmer has to watch out to always free the resources after using them, because otherwise the program could leak memory or unnecessarily inhibit other processes from accessing the resources. On the other hand, one may also not free the resources too early, which would lead to a use after free, and also not free resources multiple times. This can be very tricky in complex programs where resources are shared between threads or over \acs{API} boundaries.
That is why several programming languages have ways to make handling resources easier. Python has context managers that close resources automatically.
...
...
@@ -88,7 +88,7 @@ associated resources are freed. It is said that a variable has a \emph{drop resp
\subsubsection{Move Semantics}
\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, there must always be exactly one owner for every value. This means that the compiler will transfer ownership sometimes, like in the following example.
We create a pointer and use it as an argument to a function that prints the pontee’s value. After that we increment the value. But if we try to compile this, we get an error:
We create a pointer and use it as an argument to a function that prints the pointee’s value. After that we increment the value. But if we try to compile this, we get an error:
\begin{lstlisting}
error[E0382]: use of moved value: `*ptr`
...
...
@@ -161,7 +161,7 @@ Sometimes we don’t want \ac{RAII} to happen, we want to free resources ourselv
As we have seen, ownership and move semantics ensure memory safety. But they are not easy to use, \inline{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 \inline{print_value} as a reference using \inline{&}, and creating a reference from a value works the same.
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 sufficient. We mark the argument to \inline{print_value} as a reference using \inline{&}, and creating a reference from a value works the same.
\begin{lstlisting}[language=Rust, caption={Borrow using references to prevent a move}, label={lst:borrow}]
fn print_value(ptr: &Box<i32>) {
...
...
@@ -180,7 +180,7 @@ fn main() {
\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 \inline{&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 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 pointee 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}
...
...
@@ -192,15 +192,15 @@ References have to agree to these rules, but the owner has to as well. While sha
\todo[inline]{
\textbf{This doesn’t not work, I have to think about this some more. I want something to throw 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 \inline{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}
}
\subsubsection{Lexical Lifetimes and Lifetime Analysis}
...
...
@@ -305,7 +305,7 @@ Sometimes, we have a reference to one type but need a reference to another, simi
\subsubsection{AsRef}
Generally, there are many types that can act as a substitute for another. The common interface for this behavior is the \inline{AsRef} trait. There can be many implementations of this trait for a given type. for example, \inline{String} can stand in for \inline{str}, \inline{[u8]}, \inline{OsStr} and \inline{Path}. Everytime a reference to one of these types are needed, we can use a \inline{String} instead, if we first call \inline{as_ref} on it:
Generally, there are many types that can act as a substitute for another. The common interface for this behavior is the \inline{AsRef} trait. There can be many implementations of this trait for a given type. for example, \inline{String} can stand in for \inline{str}, \inline{[u8]}, \inline{OsStr} and \inline{Path}. Everytime a reference to one of these types are needed, we can use a \inline{String} instead, if we first call \inline{as_ref} on it:
\begin{lstlisting}[language=Rust, caption={Use \inline{String} in place of \inline{[u8]}}, label={lst:as_ref_bytes}]
fn needs_bytes(x: &[u8]) { /*...*/ }
...
...
@@ -318,7 +318,7 @@ needs_bytes(s.as_ref());
\subsubsection{Deref}
Always calling \inline{as_ref} is a bit cumbersome, expecially if it is clear which type the target should be. For example, if we have a \inline{Box<T>} there is really only one reasonable type we want to get out of it, namely a \inline{&T}. The trait \inline{Deref} can be used for that. It specifies a single type which Rust automatically converts into if it is needed. For example, \inline{String} has \inline{str} as its Deref target which allows us to omit the \inline{as_ref} in that case and leave the conversion implicit:
Always calling \inline{as_ref} is a bit cumbersome, especially if it is clear which type the target should be. For example, if we have a \inline{Box<T>} there is really only one reasonable type we want to get out of it, namely a \inline{&T}. The trait \inline{Deref} can be used for that. It specifies a single type which Rust automatically converts into if it is needed. For example, \inline{String} has \inline{str} as its \inline{Deref} target which allows us to omit the \inline{as_ref} in that case and leave the conversion implicit:
\begin{lstlisting}[language=Rust, caption={Use \inline{String} in place of \inline{[u8]}}]
fn needs_str(x: &str) { /*...*/ }
...
...
@@ -329,7 +329,7 @@ let s = String::from("Hello Bytes");
needs_bytes(s);
\end{lstlisting}
We couldn’t do the same in \autoref{lst:as_ref_bytes} because \inline{[u8]} is not Deref target of String.
We couldn’t do the same in \autoref{lst:as_ref_bytes} because \inline{[u8]} is not \inline{Deref} target of \inline{String}.
\subsubsection{Borrow}
...
...
@@ -383,7 +383,7 @@ if xref != &0 {
x = 42;
\end{lstlisting}
Now the \ac{CFG} (\autoref{fig:cfg-non-lexical-lifetimes-branch}) splits up to accomodate both possible paths the program could take during execution. At the \inline{if}, the graph splits into two, but in the last node, both paths join up again. Here we can see that in the \inline{false} branch no problem occurs, but in the \inline{true} branch we try to modify \inline{x} even though \inline{xref} is used later in the same path. Modifying \inline{x} on the last line is okay, though.
Now the \ac{CFG} (\autoref{fig:cfg-non-lexical-lifetimes-branch}) splits up to accommodate both possible paths the program could take during execution. At the \inline{if}, the graph splits into two, but in the last node, both paths join up again. Here we can see that in the \inline{false} branch no problem occurs, but in the \inline{true} branch we try to modify \inline{x} even though \inline{xref} is used later in the same path. Modifying \inline{x} on the last line is okay, though.
\begin{figure}[!h]
\centering
...
...
@@ -412,8 +412,8 @@ Now the \ac{CFG} (\autoref{fig:cfg-non-lexical-lifetimes-branch}) splits up to a
\subsection{Reborrows, the Borrow Stack and Two-Phase Borrowing}
\begin{enumerate}
\item What is a reborrow?
\item Example: Reborrows are correct but rejected
\item Solution: Allow for Reborrows
\item Example: reborrows are correct but rejected
\item Solution: Allow for reborrows
\item Using shared references after creating a unique one is correct but rejected
\item Example
\item Solution: Two-Phase Borrowing
...
...
@@ -426,7 +426,7 @@ Now the \ac{CFG} (\autoref{fig:cfg-non-lexical-lifetimes-branch}) splits up to a
\item regions as an alternative to lifetimes
\item regions are more powerful
\item Piggyback on the talk by \href{https://www.youtube.com/watch?v=_agDeiWek8w}{Niko Matsakis}
\item Note: Polonius will be a new Borrowchecker using regions but it is unstable
\item Note: Polonius will be a new borrowchecker using regions but it is unstable
\item Note: concrete memory regions à la Cyclone vs abstract regions à la Polonius