I'm working through the ownership section.
I wrote the following code an expected an error.
Question: Why isn't there an error in the first compiling version?
This one compiles:
fn main () {
let mut x = String::from("vier");
let mut y = x.to_owned()+"1"; //here x should go out of scope, because used in a method
let mut z = x.to_owned()+"2";
x.push_str("test");
println!("{}", x);
println!("{}", y);
println!("{}", z);
}
This one does not compile:
fn main () {
let mut x = String::from("vier");
let mut y = x;//x out of scope
let mut z = x;//x out of scope
x.push_str("test");
println!("{}", x);
println!("{}", y);
println!("{}", z);
}
to_owned() only takes a reference, not ownership. You'll notice that the blanket implementation for T requires T: Clone:
impl<T> ToOwned for T where
T: Clone,
type Owned = T
That indicates that .to_owned() uses .clone() for any type that implements Clone.
to_owned is a method from borrow::ToOwned trait. Which returns an owned version (cloned/copy) of the element. It just takes an &self so the object is not really moved.
Related
I am trying to write a mem::swap function without using any std lib functions. i am totally new to rust and still trying to make sense of how to use rust language.
below is my code
fn swap<T: std::fmt::Display>(x: &mut T, y: &mut T) {
unsafe {
// Give ourselves some scratch space to work with
let mut t: &mut T = y;
y = x;
x = t;
}
}
fn main() {
println!("Hello, world!");
let mut x = Box::new(5);
let mut y = Box::new(42);
let mut t = Box::new(0);
swap(&mut x, &mut y);
}
and i am facing below error
error: lifetime may not live long enough
--> src/main.rs:4:29
|
1 | fn swap<T: std::fmt::Display>(x: &mut T, y: &mut T) {
| - - let's call the lifetime of this reference `'2`
| |
| let's call the lifetime of this reference `'1`
...
4 | let mut t: &mut T = y;
| ^ assignment requires that `'2` must outlive `'1`
|
help: consider introducing a named lifetime parameter
|
1 | fn swap<'a, T: std::fmt::Display>(x: &'a mut T, y: &'a mut T) {
| +++ ++ ++
what does 'lifetime may not live long enough' mean ?
is there a simple way to write mem::swap code in rust ?
You need to Copy the data. The reference you're using is useless for this purpose. You need to actually alter what both x and y reference.
For example:
fn swap<T>(a: &mut T, b: &mut T) where T : Copy {
(*a,*b) = (*b, *a)
}
fn main() {
let mut a = 1;
let mut b = 2;
swap(&mut a,&mut b);
println!("a={}, b={}", a, b);
}
If you set up the conditions here, it's really a one-liner, Rust will figure out the "temporary" stuff for you. In fact, having a function to do this is actually kind of overkill, since you can just do that single line anywhere in your code as you would normally.
Maybe you want to optimize this around boxed values, taking Box<T> as an argument instead, in which case you could swap references within the box instead of copying, but that's a specialization.
How about using core::ptr::swap to swap raw pointers *mut T.
use core::ptr::swap;
fn custom_swap<T>(x: &mut T, y: &mut T) {
unsafe {
(x as *mut T).swap(y as *mut T);
}
}
fn main() {
println!("Hello, world!");
let mut x = Box::new(5);
let mut y = Box::new(42);
println!("x={}, y={}", x, y);
custom_swap(&mut x, &mut y);
println!("x={}, y={}", x, y);
}
output
Hello, world!
x=5, y=42
x=42, y=5
The following code doesn't compile, because x is used after move (since x has type &mut u8, which does not implement the Copy trait)
fn main() {
let mut a: u8 = 1;
let x: &mut u8 = &mut a;
let y = x;
x;
}
And as I understand y implicitly has type &mut u8
But if I specify the type of y explicitly it compiles. The following code compiles
fn main() {
let mut a: u8 = 1;
let x: &mut u8 = &mut a;
let y: &mut u8 = x;
x;
}
By the way if I change let y: &mut u8 = x; to let y: &u8 = x; it also compiles.
It seems to me nothing has changed, y is &mut u8 in the first example and it is &mut u8 in the second but the former doesn't compile and the latter does compile.
Why? What's the difference?
&mut u8 is not a complete type. All reference types must have a lifetime parameter on them. When you elide it, the Rust compiler must infer it, and it will pick the shortest lifetime possible. In your first example, the first step of desugaring is (using a fake syntax from the Rustonomicon):
fn main() {
let mut a: u8 = 1;
'a: {
let x: &'a mut u8 = &'a mut a; // the lifetime of x must last until it's use at the end
let y = x;
x;
}
}
This is still not completely explicit, since the type of y is still not known. What is it? Well, since it's being assigned from x, which is a &'a mut u8, it should also be a &'a mut u8. Note that this is not following the "shortest lifetime possible" rule of lifetime elision. You didn't elide a lifetime, you elided the whole type, which is reconstructed by type inference.
fn main() {
let mut a: u8 = 1;
'a: {
let x: &'a mut u8 = &'a mut a; // the lifetime of x must last until its use at the end
let y: &'a mut u8 = x;
x;
}
}
Well, that's no good. Since y has the same lifetime as x, its creation involves moving the reference in x and making x invalid. The program is thus rejected for trying to use x.
Adding the signature to y essentially gives the compiler a new place where it can infer lifetimes. Before, normal type inference made y have the same type as x, which meant it lasted as long as x and made x unusable. Now, y doesn't need to have the same type as x; the lifetime of the borrow can be different. In particular, it is made shorter.
fn main() {
let mut a: u8 = 1;
'a: {
let x: &'a mut u8 = &'a mut a; // the lifetime of x must last until it's use at the end
'b: {
let y: &'b mut u8 = x; // y and x can now have different lifetimes, *x is reborrowed here
}
x; // x not moved from, still valid
}
}
Now, instead of moving the reference x into y and making it invalid, the value *x is temporarily "reborrowed" to make y, as by let y: &'b mut u8 = &'b mut *x.
The other possible fix is to explicitly say "borrow *x again with a different lifetime":
fn main() {
let mut a: u8 = 1;
let x: &mut u8 = &mut a;
let y = &mut *x;
x;
}
The principle is the same as before: saying & more often gives the compiler more places where it can massage the lifetimes in the program to make everything work.
I have a function f that accepts two references, one mut and one not mut. I have values for f inside a HashMap:
use std::collections::HashMap;
fn f(a: &i32, b: &mut i32) {}
fn main() {
let mut map = HashMap::new();
map.insert("1", 1);
map.insert("2", 2);
{
let a: &i32 = map.get("1").unwrap();
println!("a: {}", a);
let b: &mut i32 = map.get_mut("2").unwrap();
println!("b: {}", b);
*b = 5;
}
println!("Results: {:?}", map)
}
This doesn't work because HashMap::get and HashMap::get_mut attempt to mutably borrow and immutably borrow at the same time:
error[E0502]: cannot borrow `map` as mutable because it is also borrowed as immutable
--> src/main.rs:15:27
|
12 | let a: &i32 = map.get("1").unwrap();
| --- immutable borrow occurs here
...
15 | let b: &mut i32 = map.get_mut("2").unwrap();
| ^^^ mutable borrow occurs here
...
18 | }
| - immutable borrow ends here
In my real code I'm using a large, complex structure instead of a i32 so it is not a good idea to clone it.
In fact, I'm borrowing two different things mutably/immutably, like:
struct HashMap {
a: i32,
b: i32,
}
let mut map = HashMap { a: 1, b: 2 };
let a = &map.a;
let b = &mut map.b;
Is there any way to explain to the compiler that this is actually safe code?
I see how it possible to solve in the concrete case with iter_mut:
{
let mut a: &i32 = unsafe { mem::uninitialized() };
let mut b: &mut i32 = unsafe { mem::uninitialized() };
for (k, mut v) in &mut map {
match *k {
"1" => {
a = v;
}
"2" => {
b = v;
}
_ => {}
}
}
f(a, b);
}
But this is slow in comparison with HashMap::get/get_mut
TL;DR: You will need to change the type of HashMap
When using a method, the compiler does not inspect the interior of a method, or perform any runtime simulation: it only bases its ownership/borrow-checking analysis on the signature of the method.
In your case, this means that:
using get will borrow the entire HashMap for as long as the reference lives,
using get_mut will mutably borrow the entire HashMap for as long as the reference lives.
And therefore, it is not possible with a HashMap<K, V> to obtain both a &V and &mut V at the same time.
The work-around, therefore, is to avoid the need for a &mut V entirely.
This can be accomplished by using Cell or RefCell:
Turn your HashMap into HashMap<K, RefCell<V>>,
Use get in both cases,
Use borrow() to get a reference and borrow_mut() to get a mutable reference.
use std::{cell::RefCell, collections::HashMap};
fn main() {
let mut map = HashMap::new();
map.insert("1", RefCell::new(1));
map.insert("2", RefCell::new(2));
{
let a = map.get("1").unwrap();
println!("a: {}", a.borrow());
let b = map.get("2").unwrap();
println!("b: {}", b.borrow());
*b.borrow_mut() = 5;
}
println!("Results: {:?}", map);
}
This will add a runtime check each time you call borrow() or borrow_mut(), and will panic if you ever attempt to use them incorrectly (if the two keys are equal, unlike your expectations).
As for using fields: this works because the compiler can reason about borrowing status on a per-field basis.
Something appears to have changed since the question was asked. In Rust 1.38.0 (possibly earlier), the following compiles and works:
use std::collections::HashMap;
fn f(a: &i32, b: &mut i32) {}
fn main() {
let mut map = HashMap::new();
map.insert("1", 1);
map.insert("2", 2);
let a: &i32 = map.get("1").unwrap();
println!("a: {}", a);
let b: &mut i32 = map.get_mut("2").unwrap();
println!("b: {}", b);
*b = 5;
println!("Results: {:?}", map)
}
playground
There is no need for RefCell, nor is there even a need for the inner scope.
Vec<T> has two methods:
fn push(&mut self, value: T)
fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T])
They both take a mutable reference to the vector. But the scope of the borrow seems to be different, e.g:
fn works() {
let mut nums: Vec<i64> = vec![1,2,3,4];
nums.push(5);
println!("{}", nums.len());
}
fn doesnt_work() {
let mut nums: Vec<i64> = vec![1,2,3,4];
let (l,r) = nums.split_at_mut(2);
println!("{}", nums.len());
}
fn also_works() {
let mut nums: Vec<i64> = vec![1,2,3,4];
let _ = nums.split_at_mut(2);
println!("{}", nums.len());
}
The doesnt_work function doesn't compile, saying there is already a mutable borrow on nums and that it ends and the end of the function. The problem goes away if I ignore the values returned from split_at_mut.
The borrowing of nums in doesnt_work will last as long as the variables l and r exist because the values in the vector (and the vector itself) have literally been borrowed and are now accessible only through l and r.
You can see this effect by putting the let for l and r in a scope which ends so the borrow also ends. For example this code works fine but if you try to move the println! inside the scope (inside the curly brackets) then it will fail:
fn works() {
let mut nums = vec![1,2,3,4];
{
let (l, r) = nums.split_at_mut(2);
//println!("{}", nums.len()); //println! will fail here
}
println!("{}", nums.len());
}
In your also_works example you don't do anything with the result so the borrow is lost immediately. Basically the compiler can see that there is no way you can access the vector through the result of the method so you're free to access them through the original vector.
Let me answer my own question, since what I was really missing were lifetimes. This code compiles:
fn maybe_use<'a, 'b>(v1: &'a mut Vec<i64>, v2: &'b mut Vec<i64>) -> &'a mut Vec<i64> {
v1
}
fn main() {
let mut nums1: Vec<i64> = vec![1,2,3,4];
let mut nums2: Vec<i64> = vec![1,2,3,4];
let ret = maybe_use(&mut nums1, &mut nums2);
println!("{}", nums2.len());
}
Because the return type of maybe_use makes it clear the reference comes from the first argument. If we change v2 to use 'a lifetime, main stops compiling because both vectors passed to maybe_use are considered borrowed. If we omit the lifetime altogether, compiler emits this error:
this function's return type contains a borrowed value, but the
signature does not say whether it is borrowed from v1 or v2
So what surprised me initially (how does the compiler know split_at_mut returns pointers to the vector?) boils down to the references having the same lifetime.
fn t(x: &mut u8) -> &mut u8 {
x
}
fn main() {
let mut x = 5u8;
let y = & mut x;
let z = t(y);
println!("{}", y);
}
Compiling this gives me this error:
main.rs:9:20: 9:21 error: cannot borrow `y` as immutable because `*y` is also borrowed as mutable
main.rs:9 println!("{}", y);
I would have thought y would have been moved during the call to t and then back to z, resulting in an error: use of moved value
Why do I get this error message instead?
Does Rust automatically create a new borrow instead of passing ownership when references are offered as function parameters?
What is the purpose of this behaviour?
You are returning a mutable reference to the parameter from your function. However, Rust doesn't know that the method hasn't kept a copy of that pointer didn't return a subsection of that pointer, were it a struct. This means that at any time, the value pointed to might be changed, which is a big no-no in Rust; if it were allowed, then you could easily cause memory errors.
Does Rust automatically create a new borrow
Yes, Rust "re-borrows" references.
A better example requires a smidge more complexity:
struct Thing { a: u8, b: u8 }
fn t(x: &mut Thing) -> &mut u8 {
&mut x.a
}
fn main() {
let mut x = Thing { a: 5, b: 6 };
let z = t(&mut x);
*z = 0;
// x.a = 0; // cannot assign to `x.a` because it is borrowed
}
Here, t returns a mutable pointer to a subset of the struct. This means that the entire struct is borrowed, and we cannot change it (except via z). Rust applies this logic to all functions, and doesn't try to recognize that your t function just returns the same pointer.
By compiling your program with rustc --pretty=expanded, we can see that the println! macro borrows its argument:
#![no_std]
#[macro_use]
extern crate "std" as std;
#[prelude_import]
use std::prelude::v1::*;
fn t(x: &mut u8) -> &mut u8 { x }
fn main() {
let mut x = 5u8;
let y = &mut x;
let z = t(y);
::std::io::stdio::println_args(::std::fmt::Arguments::new({
#[inline]
#[allow(dead_code)]
static __STATIC_FMTSTR:
&'static [&'static str]
=
&[""];
__STATIC_FMTSTR
},
&match (&y,) { // <----- y is borrowed here
(__arg0,)
=>
[::std::fmt::argument(::std::fmt::String::fmt,
__arg0)],
}));
}