I'm still new to rust, so I'm not sure why there is an error here.
Ok(Value::Object(m)) =>
...
let colours: Vec<&'static CardColour>;
//Note: type annotation requires that `m` is borrowed for `'static`
...
let colour_res: Result<Vec<&CardColour>, _> =
if let Some(Value::Array(v)) = m.get("cardid") {
/*
Error here:
`m` does not live long enough
borrowed value does not live long enough
*/
v
.into_iter()
.map(|v|{
match v {
Value::String(s) => {
if let Some(cc) = CardColour::from_name(s.as_str()){
Ok(cc.clone())
} else {
Err(())
}
},
_ => Err(())
}
})
.collect()
} else {
Err(())
};
if let Ok(cc) = colour_res {
colours = cc
} else {
return Err(String::from("Missing or invalid colours"));
};
...
//Note: `m` dropped here while still borrowed
//(End of function)
As near as I can tell, m stops being used once colour_res is assigned.
CardColour::from_name is defined as follows
impl CardColour{
fn from_name (s: &str) -> Option<&CardColour>{
card_colours.get(s.to_lowercase().as_str()).cloned()
}
}
So, by my reckoning, the value extracted from m should now be finished with and it should no longer be missing.
Am I missing something obvious here? Why is m "still borrowed" at the end of the function even though the values extracted have been used and finsished with?
Am I missing something obvious here? Why is m "still borrowed" at the end of the function even though the values extracted have been used and finsished with?
The problem is this:
fn from_name (s: &str) -> Option<&CardColour>
Because you don't specify lifetimes, Rust's lifetime elision rules simply consider that the output lifetime is the same as the input:
If there is exactly one lifetime used in the parameters (elided or not), that lifetime is assigned to all elided output lifetimes.
Therefore the &CardColour is considered to keep the &str borrow alive, and since that comes from m it requires the borrow on m.
This is valid because 'static is a superset of all lifetimes, and thus a 'static lifetime can be "cast" to any other with no error.
The solution is to explicitly specify that the output lifetime is 'static.
Trivial demo:
fn foo(_: &str) -> &u8 {
&1
}
fn main() {
let v = foo(&String::new());
println!("{}", v);
}
fixed version:
struct Foo;
fn foo(_: &str) -> &'static u8 {
&1
}
fn main() {
let v = foo(&String::new());
println!("{}", v);
}
Related
This code compiles:
fn main() {
let mut s = String::from("some_string");
let n = f1(&s);
s.clear();
println!("n = {}", n);
}
fn f1(s: &String) -> usize {
10
}
fn f2(s: &String) -> &str {
"def"
}
However, replacing the call to f1() by f2() causes a compilation failure. It appears to me that both f1() and f2() do an immutable borrow, and s.clear() does a mutable borrow, so I should get compilation error in both cases. What am I missing?
Here is the minimum code necessary to reproduce the issue:
fn f1(s: &String) -> usize { unimplemented!() }
fn f2(s: &String) -> &str { unimplemented!() }
fn main() {
let mut s = String::from("some_string");
let n = f1(&s);
s.clear();
println!("n = {}", n);
}
Lifetime analysis is performed based on function signatures.
You will note in the above code that I have used unimplemented!() as the body of the functions, and the problem is exactly the same. This is normal.
In the vast majority of cases1, a function signature fully specifies the interface of a function, and it is unnecessary to look at its implementation.
As a corollary, this also means that whether the lifetime in a return type is linked to the lifetime in any of the arguments is fully specified within the signature, and in this case the full signature of f2 is therefore:
fn f2<'a>(s: &'a String) -> &'a str;
Whether the implementation of f2 is "def" (with the 'static lifetime) or &*s (with the 'a lifetime) does not matter; only the signature matters, and the signature uses the same lifetime due to the elision rules.
1 The one exception I know of concerns the -> impl Trait feature and whether the resulting object implements Send or Sync.
In the case of f1, the return type is not linked to the argument, therefore the borrow of the argument ends at the end of the call to f1:
fn main() {
let mut s = String::from("some_string");
let n = {
// Immutable borrow of s starts here.
f1(&s)
// Immutable borrow of s ends here.
};
s.clear();
println!("n = {}", n);
}
In the case of f2, the return type has the same lifetime as the argument, and therefore is considered to extend the borrow. In Rust 2015, the borrow would extend until the return value went out of scope (lexical borrow); with Rust 2018, the borrow extends until the last use of the return value (non-lexical borrow).
In your case, both are basically identical:
fn main() {
let mut s = String::from("some_string");
let n = {
// Immutable borrow of s starts here.
f2(&s)
};
s.clear(); // Conflicting attempt to mutably borrow s.
println!("n = {}", n);
// Immutable borrow of s ends here.
}
You could observe the difference by switching the order of s.clear() and println!.
The Rust reference says:
If there is exactly one lifetime used in the parameters (elided or not), that lifetime is assigned to all elided output lifetimes.
This means that your method
fn f2(s: &String) -> &str {
"def"
}
is interpreted by Rust as:
fn f2<'a>(s: &'a String) -> &'a str {
"def"
}
Since "def" has the lifetime 'static, its lifetime can be shortened to 'a when being returned from the function (so the compiler won't complain here), but when calling the function the compiler cannot infer that the true lifetime of the string was really 'static. To do this, you must explicitly mark it as 'static yourself:
fn f2(s: &String) -> &'static str {
"def"
}
f1 and f2 both take an immutable borrow. However, the lifetime of the borrow from f1 ends at the end of f1, because you're just returning a usize and not anything from the actual string.
However, f2 returns a &str, which is borrowing your underlying String, s. Since n stays alive, the immutable borrow of s continues until n is no longer used. Effectively, this prevents your call to s.clear() from "pulling the rug out from under" your pointer s.
Why does this happen in this certain scenario? I'm new to Rust, read the 2nd edition book, but.. well, yeah, here I am. :)
fn main() {
Xyz::new(&"whatever=123");
}
pub struct Xyz<'a> {
x: &'a str
}
impl<'a> Xyz<'a> {
pub fn new(query: &str) -> Result<Xyz<'a>, &'a str> {
let mut qkey: String = String::new();
let mut qval: String = String::new();
let mut is_key = true;
for (i, c) in query.chars().enumerate().skip(1) {
if c == '=' {
is_key = false;
} else if c == '&' {
is_key = true;
} else if is_key {
qkey.push(c);
} else {
qval.push(c);
}
if c == '&' || i == query.len() - 1 {
match qkey.as_ref() {
"whatever" => {
let _whatever = Xyz::some_func(&mut qval)?;
}
_ => (),
}
qkey.clear();
qval.clear();
}
}
Ok(Xyz {
x: &""
})
}
fn some_func(_val: &mut String) -> Result<bool, &str> {
Ok(true)
}
}
playground
Error:
error[E0597]: `qval` does not live long enough
--> src/main.rs:29:61
|
29 | let _whatever = Xyz::some_func(&mut qval)?;
| ^^^^ borrowed value does not live long enough
...
41 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 9:1...
--> src/main.rs:9:1
|
9 | impl<'a> Xyz<'a> {
| ^^^^^^^^^^^^^^^^
I don't understand why the actual error happens. I know the note should help me understand the problem, but it doesn't.
I don't need qval outside of this function, so why do I have to still keep it? Is there a conceptual error I made?
A trimmed down reproduction of your issue is
pub struct Xyz<'a> {
x: &'a str
}
impl<'a> Xyz<'a> {
pub fn new(_query: &str) -> Result<Xyz<'a>, &'a str> {
let qval: String = String::new();
Err(&qval)
}
}
with the error
error[E0597]: `qval` does not live long enough
--> src/main.rs:13:18
|
13 | Err(&qval)
| ^^^^ borrowed value does not live long enough
14 | }
| - borrowed value only lives until here
To understand this, I'd recommend taking a step back and thinking about what exactly your function is doing. You create a String, and then take a reference to it, and then return that reference.
This is would be a classic case of use-after-free. From the standpoint of the language, the string no longer exists, so returning a reference to it does not make sense.
Returning &str could make sense, but only if you can guarantee that the &str will only reference data that is still in scope, even after the function has returned. For instance, it would be valid to do Err(&query) (or any subsection of query) if you adjust the function to be fn new(query: &'a str) ->, since then it is it is known that the return value lives as long as the input string.
It is hard to tell from your example if that would be acceptable for usecase.
The simplest would indeed be to return a String (or Result<Xyz<'a>, String>> in your case, possibly with a String in the field for Xyz too). You could also consider returning something like a Cow which is sometimes a subsection of query or a static string, and sometimes a String.
As a side-note, I'll also add that Xyz::new(&"whatever=123"); does not need the & since it is already a reference. You can do
Xyz::new("whatever=123");
just fine. Same for &"".
My confusion was that - in the not stripped down version - both function (new and some_func returned a global &'static str as Err, so I thought the compiler would know that the string would always exist.
This may be the core of the misunderstanding. When you declare
fn some_func(_val: &mut String) -> Result<bool, &str> {
the &str is not 'static. Rust's lifetime elision logic means that this declaration will assume that the reference in the function argument and the reference in the return value have the same lifetime, meaning that what you've declared is
fn some_func<'b>(_val: &'b mut String) -> Result<bool, &'b str> {
which triggers the error you are seeing. If you want it to be static, you'll need to say so, as
fn some_func(_val: &mut String) -> Result<bool, &'static str> {
which would avoid the error, and require that some_func always returns a 'static string.
While taking a look at Rust I noticed a behavior I do not quite understand.
I've got this code, which works as expected:
fn get_or_create_foo(v: &mut Vec<String>) -> String {
match v.get(0) {
Some(x) => return x.clone(),
None => ()
}
println!("creating foo");
v.push("foo".to_string());
v.get(0).unwrap().clone()
}
fn main() {
let mut v = Vec::new();
println!("{}", get_or_create_foo(&mut v));
println!("{}", get_or_create_foo(&mut v));
}
When I change the get_or_create_foo() to make it return a borrowed string slice, the compiler refuses to compile it.
fn get_or_create_foo(v: &mut Vec<String>) -> &str {
match v.get(0) {
Some(x) => return x,
None => ()
}
println!("creating foo");
v.push("foo".to_string());
v.get(0).unwrap()
}
Compilation log:
$ rustc --verbose src/main.rs
src/main.rs:8:5: 8:6 error: cannot borrow `*v` as mutable because it is also borrowed as immutable
src/main.rs:8 v.push("foo".to_string());
^
src/main.rs:2:11: 2:12 note: previous borrow of `*v` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `*v` until the borrow ends
src/main.rs:2 match v.get(0) {
^
src/main.rs:10:2: 10:2 note: previous borrow ends here
src/main.rs:1 fn get_or_create_foo(v: &mut Vec<String>) -> &str {
...
src/main.rs:10 }
^
error: aborting due to previous error
In my understanding that code is valid: the mentioned borrow may be returned as soon as control leaves the match clause by taking the path leading to the code mutating v.
Am I wrong? Could anyone give an example when allowing such code would cause problems?
I don't know exactly, but I suspect that your code:
fn get_or_create_foo(v: &mut Vec<String>) -> &str {
match v.get(0) {
Some(x) => return x,
None => ()
}
println!("creating foo");
v.push("foo".to_string());
v.get(0).unwrap()
}
is translated by the compiler into something with equivalent syntax by eliminating explicit return, like this:
fn get_or_create_foo(v: &mut Vec<String>) -> &str {
match v.get(0) {
Some(x) => x,
None => {
println!("creating foo");
v.push("foo".to_string());
v.get(0).unwrap()
},
}
}
which obviously fails with the same error. Here get produces Option<&String>, so v remains borrowed even in None branch, where no reference is captured.
Fortunately, there is an easy way to rewrite the function:
fn get_or_create_foo(v: &mut Vec<String>) -> &str {
if v.get(0).is_none() {
println!("creating foo");
v.push("foo".to_string());
}
v.get(0).unwrap()
}
You can slightly improve swizard's solution:
fn get_or_create_foo(v: &mut Vec<String>) -> &str {
if v.is_empty() {
println!("creating foo");
v.push("foo".to_string());
}
&v[0]
}
I am new to Rust myself too, but I believe I might have found the source of your problem.
You can inspect the type signature of "get" function here. As you can see, "get" function returns a borrowed reference to the requested member of the vector (wrapped inside Option). My guess is that the compiler is unable to verify in your situation that "x" can not "escape" from match block.
Here is a simpler, but similar example from A 30-minute Introduction to Rust:
fn main() {
let mut v = vec![];
v.push("Hello");
let x = &v[0];
v.push("world");
println!("{}", x);
}
In Rust, the type system encodes the notion of ownership. The variable v is an owner of the vector. When we make a reference to v, we let that variable (in this case, x) borrow it for a while. Just like if you own a book, and you lend it to me, I'm borrowing the book.
So, when I try to modify the vector with the second call to push, I need to be owning it. But x is borrowing it. You can't modify something that you've lent to someone. And so Rust throws an error.
Here is how I am imaging it:
fn get_or_create_foo(v: &mut Vec<String>) -> &str {
let a: &str;
match v.get(0) {
Some(x) => {
a = x;
return x;
},
None => ()
}
// Now "a" is still borrowing "v" immutably!
// println!("{:?}", a);
println!("creating foo");
v.push("foo".to_string());
v.get(0).unwrap()
}
As I said I'm still a beginner so there might be more to this. I came to my conclusion after toying around with your code a bit.
A simple refactor would fix this problem:
fn get_or_create_foo(v: &mut Vec<String>) -> &str {
match v.get(0) {
// Notice how the borrowed value is never used and
// thus can not "escape" our match block.
Some(_) => (),
_ => v.push("foo".to_string())
}
// No need to use "get" here since we are 100% sure that
// the indexed vector contains at least one item.
return &v[0];
}
I have some code that, simplified, looks like this:
enum A<'a> {
AConst(&'a [u8])
}
trait FromA {
fn from_a(A) -> Self;
}
impl FromA for &[u8] {
fn from_a(a: A) -> &[u8] {
match a {
AConst(bytes) => bytes
}
}
}
fn main() {
// I'd like to use it like this:
let s = b"abc";
let a = AConst(s);
let foo: &[u8] = from_a(a);
}
This doesn't work, as the compiler complains about missing lifetime specifiers on the &[u8]. Now I'm not sure what the correct lifetime would be. As from_a consumes its argument, the lifetime of the returned reference clearly cannot be the same as the lifetime of the argument.
Is it possible to somehow use lifetime annotations to achieve this? If it is, what would the correct annotations be? Can we somehow make the A type carry information about the lifetime of the reference?
Can we somehow make the A type carry information about the lifetime of the reference?
That is in fact exactly what you are doing when writing
enum A<'a> { //'
AConst(&'a [u8]) //'
}
The full type here is A<'a> meaning that A carries inside it a reference of lifetime 'a.
To be correct, you need to propagate explicitly this lifetime in your trait definition and implementation :
trait FromA<'a> { //'
fn from_a(A<'a>) -> Self; //'
}
impl<'a> FromA<'a> for &'a [u8] { //'
fn from_a(a: A<'a>) -> &'a [u8] {
match a {
AConst(bytes) => bytes
}
}
}
Thus saying : The lifetime of the &[u8] slice is the lifetime of the reference contained in the A object.
You can then do :
fn main() {
let s = b"abc";
let a = AConst(s);
let foo: &[u8] = FromA::from_a(a);
println!("{}", foo);
}
[97, 98, 99]
I'm getting a Rust compile error from the borrow checker, and I don't understand why. There's probably something about lifetimes I don't fully understand.
I've boiled it down to a short code sample. In main, I want to do this:
fn main() {
let codeToScan = "40 + 2";
let mut scanner = Scanner::new(codeToScan);
let first_token = scanner.consume_till(|c| { ! c.is_digit ()});
println!("first token is: {}", first_token);
// scanner.consume_till(|c| { c.is_whitespace ()}); // WHY DOES THIS LINE FAIL?
}
Trying to call scanner.consume_till a second time gives me this error:
example.rs:64:5: 64:12 error: cannot borrow `scanner` as mutable more than once at a time
example.rs:64 scanner.consume_till(|c| { c.is_whitespace ()}); // WHY DOES THIS LINE FAIL?
^~~~~~~
example.rs:62:23: 62:30 note: previous borrow of `scanner` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `scanner` until the borrow ends
example.rs:62 let first_token = scanner.consume_till(|c| { ! c.is_digit ()});
^~~~~~~
example.rs:65:2: 65:2 note: previous borrow ends here
example.rs:59 fn main() {
...
example.rs:65 }
Basically, I've made something like my own iterator, and the equivalent to the "next" method takes &mut self. Because of that, I can't use the method more than once in the same scope.
However, the Rust std library has an iterator which can be used more than once in the same scope, and it also takes a &mut self parameter.
let test = "this is a string";
let mut iterator = test.chars();
iterator.next();
iterator.next(); // This is PERFECTLY LEGAL
So why does the Rust std library code compile, but mine doesn't? (I'm sure the lifetime annotations are at the root of it, but my understanding of lifetimes doesn't lead to me expecting a problem).
Here's my full code (only 60 lines, shortened for this question):
use std::str::{Chars};
use std::iter::{Enumerate};
#[deriving(Show)]
struct ConsumeResult<'lt> {
value: &'lt str,
startIndex: uint,
endIndex: uint,
}
struct Scanner<'lt> {
code: &'lt str,
char_iterator: Enumerate<Chars<'lt>>,
isEof: bool,
}
impl<'lt> Scanner<'lt> {
fn new<'lt>(code: &'lt str) -> Scanner<'lt> {
Scanner{code: code, char_iterator: code.chars().enumerate(), isEof: false}
}
fn assert_not_eof<'lt>(&'lt self) {
if self.isEof {fail!("Scanner is at EOF."); }
}
fn next(&mut self) -> Option<(uint, char)> {
self.assert_not_eof();
let result = self.char_iterator.next();
if result == None { self.isEof = true; }
return result;
}
fn consume_till<'lt>(&'lt mut self, quit: |char| -> bool) -> ConsumeResult<'lt> {
self.assert_not_eof();
let mut startIndex: Option<uint> = None;
let mut endIndex: Option<uint> = None;
loop {
let should_quit = match self.next() {
None => {
endIndex = Some(endIndex.unwrap() + 1);
true
},
Some((i, ch)) => {
if startIndex == None { startIndex = Some(i);}
endIndex = Some(i);
quit (ch)
}
};
if should_quit {
return ConsumeResult{ value: self.code.slice(startIndex.unwrap(), endIndex.unwrap()),
startIndex:startIndex.unwrap(), endIndex: endIndex.unwrap() };
}
}
}
}
fn main() {
let codeToScan = "40 + 2";
let mut scanner = Scanner::new(codeToScan);
let first_token = scanner.consume_till(|c| { ! c.is_digit ()});
println!("first token is: {}", first_token);
// scanner.consume_till(|c| { c.is_whitespace ()}); // WHY DOES THIS LINE FAIL?
}
Here's a simpler example of the same thing:
struct Scanner<'a> {
s: &'a str
}
impl<'a> Scanner<'a> {
fn step_by_3_bytes<'a>(&'a mut self) -> &'a str {
let return_value = self.s.slice_to(3);
self.s = self.s.slice_from(3);
return_value
}
}
fn main() {
let mut scan = Scanner { s: "123456" };
let a = scan.step_by_3_bytes();
println!("{}", a);
let b = scan.step_by_3_bytes();
println!("{}", b);
}
If you compile that, you get errors like the code in the question:
<anon>:19:13: 19:17 error: cannot borrow `scan` as mutable more than once at a time
<anon>:19 let b = scan.step_by_3_bytes();
^~~~
<anon>:16:13: 16:17 note: previous borrow of `scan` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `scan` until the borrow ends
<anon>:16 let a = scan.step_by_3_bytes();
^~~~
<anon>:21:2: 21:2 note: previous borrow ends here
<anon>:13 fn main() {
...
<anon>:21 }
^
Now, the first thing to do is to avoid shadowing lifetimes: that is, this code has two lifetimes called 'a and all the 'as in step_by_3_bytes refer to the 'a declare there, none of them actually refer to the 'a in Scanner<'a>. I'll rename the inner one to make it crystal clear what is going on
impl<'a> Scanner<'a> {
fn step_by_3_bytes<'b>(&'b mut self) -> &'b str {
The problem here is the 'b is connecting the self object with the str return value. The compiler has to assume that calling step_by_3_bytes can make arbitrary modifications, including invalidating previous return values, when looking at the definition of step_by_3_bytes from the outside (which is how the compiler works, type checking is purely based on type signatures of things that are called, no introspect). That is, it could be defined like
struct Scanner<'a> {
s: &'a str,
other: String,
count: uint
}
impl<'a> Scanner<'a> {
fn step_by_3_bytes<'b>(&'b mut self) -> &'b str {
self.other.push_str(self.s);
// return a reference into data we own
self.other.as_slice()
}
}
Now, each call to step_by_3_bytes starts modifying the object that previous return values came from. E.g. it could cause the String to reallocate and thus move in memory, leaving any other &str return values as dangling pointers. Rust protects against this by tracking these references and disallowing mutation if it could cause such catastrophic events. Going back to our actual code: the compiler is type checking main just by looking at the type signature of step_by_3_bytes/consume_till and so it can only assume the worst case scenario (i.e. the example I just gave).
How do we solve this?
Let's take a step back: as if we're just starting out and don't know which lifetimes we want for the return values, so we'll just leave them anonymous (not actually valid Rust):
impl<'a> Scanner<'a> {
fn step_by_3_bytes<'b>(&'_ mut self) -> &'_ str {
Now, we get to ask the fun question: which lifetimes do we want where?
It's almost always best to annotate the longest valid lifetimes, and we know our return value lives for 'a (since it comes straight of the s field, and that &str is valid for 'a). That is,
impl<'a> Scanner<'a> {
fn step_by_3_bytes<'b>(&'_ mut self) -> &'a str {
For the other '_, we don't actually care: as API designers, we don't have any particular desire or need to connect the self borrow with any other references (unlike the return value, where we wanted/needed to express which memory it came from). So, we might as well leave it off
impl<'a> Scanner<'a> {
fn step_by_3_bytes<'b>(&mut self) -> &'a str {
The 'b is unused, so it can be killed, leaving us with
impl<'a> Scanner<'a> {
fn step_by_3_bytes(&mut self) -> &'a str {
This expresses that Scanner is referring to some memory that is valid for at least 'a, and then returning references into just that memory. The self object is essentially just a proxy for manipulating those views: once you have the reference it returns, you can discard the Scanner (or call more methods).
In summary, the full, working code is
struct Scanner<'a> {
s: &'a str
}
impl<'a> Scanner<'a> {
fn step_by_3_bytes(&mut self) -> &'a str {
let return_value = self.s.slice_to(3);
self.s = self.s.slice_from(3);
return_value
}
}
fn main() {
let mut scan = Scanner { s: "123456" };
let a = scan.step_by_3_bytes();
println!("{}", a);
let b = scan.step_by_3_bytes();
println!("{}", b);
}
Applying this change to your code is simply adjusting the definition of consume_till.
fn consume_till(&mut self, quit: |char| -> bool) -> ConsumeResult<'lt> {
So why does the Rust std library code compile, but mine doesn't? (I'm sure the lifetime annotations are at the root of it, but my understanding of lifetimes doesn't lead to me expecting a problem).
There's a slight (but not huge) difference here: Chars is just returning a char, i.e. no lifetimes in the return value. The next method (essentially) has signature:
impl<'a> Chars<'a> {
fn next(&mut self) -> Option<char> {
(It's actually in an Iterator trait impl, but that's not important.)
The situation you have here is similar to writing
impl<'a> Chars<'a> {
fn next(&'a mut self) -> Option<char> {
(Similar in terms of "incorrect linking of lifetimes", the details differ.)
Let’s look at consume_till.
It takes &'lt mut self and returns ConsumeResult<'lt>. This means that the lifetime 'lt, the duration of the borrow of the input parameter self, will be that of the output parameter, the return value.
Expressed another way, after calling consume_till, you cannot use self again until its result is out of scope.
That result is placed into first_token, and first_token is still in scope in your last line.
In order to get around this, you must cause first_token to pass out of scope; the insertion of a new block around it will do this:
fn main() {
let code_to_scan = "40 + 2";
let mut scanner = Scanner::new(code_to_scan);
{
let first_token = scanner.consume_till(|c| !c.is_digit());
println!("first token is: {}", first_token);
}
scanner.consume_till(|c| c.is_whitespace());
}
All this does stand to reason: while you have a reference to something inside the Scanner, it is not safe to let you modify it, lest that reference be invalidated. This is the memory safety that Rust provides.