I have looked on Stack Overflow for ways to set values within a declared mutable struct. The answers did not look straightforward, and for me, strayed too far away from simplicity that I enjoy. I eventually found out from tinkering around that this code works as desired:
struct FooStruct {
bar: i32,
}
impl FooStruct {
fn set(&mut self, val: i32){
//*self.first_name_mut() = val;
*(&mut self.bar) = val;
}
}
fn main() {
let mut k = 45;
let mut my_person = FooStruct { bar: 2};
println!("Fn: {}", my_person.bar);
my_person.set(k);
println!("Fn: {}", my_person.bar); //value is printed as 45
k = 33; // set k-value
println!("Fn: {}", my_person.bar); //the value in the struct remains as 45, unchanged
}
Notice how simple it is to implement a setter:
*(&mut self.bar) = val;
My question is: is there a catch to this simplicity of setting a value? People have went as far as creating macros for getting/setting, so I'm wondering why this method isn't used. I'm coming from Java-land, where setting is a mindless deal (this.value = input); upon arrival where rustaceans live, I expected OOP-like features to be a nuisance to implement.
While I hope this post helps people implement a setter, I also want an experienced user to explain what the line of code above is doing as well as any pitfalls that I have not (possibly yet) seen.
Mutability is inherited from the parent object, in this case "my_person", because it was declared as "mut". As such, as Benjamin Lindley pointed, all you have to do is replace:
*(&mut self.bar) = val;
with:
self.bar = val;
Lesson: mutability propagates from the parent object downwards into the structure if the object is declared mutable
Related
Rust noob here reading about explicit annotation of lifetimes. I see there's lots of questions about it here, so I hope this post isn't duplicating.
For me, this answers the question why the compiler needs such annotations but does not answer why the user needs it. The example in the answer is:
struct Foo<'a> {
x: &'a i32,
}
fn main() {
let f : Foo;
{
let n = 5; // variable that is invalid outside this block
let y = &n;
f = Foo { x: y };
};
println!("{}", f.x);
}
And I can see y goes out of scope before f, but I can't think of a case where I'd want a struct with some field that could go out of scope before the parent struct? Why would anyone need that? And by extension of that question, why not just always enforce that the fields lives as long as the parent struct?
The same question applies to functions. I can't see why I would want some function that takes some borrowed variables as an argument but the variable don't live through the entire function? The only scenario I could think of is when there's some concurrency involved.
The lifetime annotation is not for Foo but for things that use the reference, outside/after it.
Take this example:
struct Foo<'a> {
x: &'a i32,
}
fn main() {
let n = 5;
let z;
{
let f = Foo { x: &n };
z = f.x;
};
println!("{}", z);
}
If Foo did not keep track of the lifetime then there would be no way for the compiler to know z is valid even after f went out of scope.
In other words the annotation is there to 'extend' the lifetime of references beyond the struct they're contained in.
I'll use the CustomSmartPointer from The Book, which is used to explain the Drop trait, to build an example:
struct CustomSmartPointer {
data: String,
}
impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println!("Dropping CustomSmartPointer with data `{}`!", self.data);
}
}
fn main() {
println!("A");
let pointer = CustomSmartPointer {
data: String::from("my stuff"),
};
println!("B");
println!("output: {}", pointer.data);
println!("C");
}
This prints:
A
B
output: my stuff
C
Dropping CustomSmartPointer with data `my stuff`!
However, from what I learned I would have expected the last two lines to be swapped. The local variable pointer isn't used anymore after being printed, so I would have expected its scope to end after the line that prints its contents, before "C" gets printed. For example, the section on references has this example:
let mut s = String::from("hello");
let r1 = &s; // no problem
let r2 = &s; // no problem
println!("{} and {}", r1, r2);
// variables r1 and r2 will not be used after this point
let r3 = &mut s; // no problem
println!("{}", r3);
So it seems like either (1) the Drop trait extends the scope until the end of the current block, or (2) it is only references whose scope ends "ASAP" and everything else already lives until the end of the current block (and that would be a major difference between references and smart pointers), or (3) something entirely else is going on here. Which one is it?
Edit: another example
I realized one thing while reading the answers so far: that (1) and (2) are equivalent if (1) includes cases where a type does not implement the Drop trait but "things happen while dropping", which is the case e.g. for a struct that contains another value that implements Drop. I tried this and it is indeed the case (using the CustomSmartPointer from above):
struct Wrapper {
csp: CustomSmartPointer,
}
fn create_wrapper() -> Wrapper {
let pointer = CustomSmartPointer {
data: String::from("my stuff"),
};
Wrapper {
csp: pointer,
}
}
fn main() {
println!("A");
let wrapper = create_wrapper();
println!("B");
println!("output: {}", wrapper.csp.data);
println!("C");
}
This still prints "Dropping CSP" last, after "C", so even a non-Drop wrapper struct that contains a value with the Drop trait has lexical scope. As hinted above, you could then equivalently say: the Drop-able value inside the struct causes a usage at the end of the block that causes the whole wrapper to be dropped only at the end of the block, or you could say that only references have NLL. The difference between the two statements is only about when a value gets dropped that is deeply free of Drop-trait values, which isn't observable.
Do not look if a type implements Drop, look at the return value of needs_drop::<T>() instead.
That said, it is option (1): a type that needs_drop() has an implicit call to drop() at the end of the lexical scope. It is this call that extends the scope of the value.
So you code is as if:
fn main() {
println!("A");
let wrapper = create_wrapper();
println!("B");
println!("output: {}", wrapper.csp.data);
println!("C");
drop(wrapper); // <- implicitly called, wrapper scope ends here
}
Naturally, you can call drop(wrapper) anywhere to end the scope prematurely. As drop() takes its argument by value, it finishes the scope there.
If the type of a value does not needs_drop(), then it is released at the last usage of that value, that is a non lexical scope (NLL).
The non-lexical scopes affects not only references, but any type that doesn't need drop. The thing is that if a value doesn't need drop and doesn't borrow anything, then its scope does not have any visible effect and nobody cares.
For example, this code has a NLL that is technically not a reference:
use std::marker::PhantomData;
#[derive(Debug)]
struct Foo<'a> { _pd: PhantomData<&'a ()> }
impl<'a> Foo<'a> {
fn new<T>(x: &'a mut T) -> Foo<'a> {
Foo { _pd: PhantomData }
}
}
fn main() {
let mut x = 42;
let f1 = Foo::new(&mut x);
let f2 = Foo::new(&mut x);
//dbg!(&f1); // uncomment this line and it will fail to compile
}
It's option two. Implementing Drop trait means that additional actions will happen when object is dropped. But everything will be at some point dropped, whether it implements Drop or not.
println! is not taking your pointer
playgroud...
However, from what I learned I would have expected the last two lines to be swapped. The local variable pointer isn't used anymore after being printed, so I would have expected its scope to end after the line that prints its contents, before "C" gets printed. For example, the section on references has this example:
let mut s = String::from("hello");
let r1 = &s; // no problem
let r2 = &s; // no problem
println!("{} and {}", r1, r2);
// variables r1 and r2 will not be used after this point
let r3 = &mut s; // no problem
println!("{}", r3);
The thing here is that s is never taked, r1 and r2 are dropped after r3 because it takes a mutable reference to s
playground
In Pin, we disable as_mut so we can not get a &mut for functions like std::mem::swap that will move the object.
However, somehow we need interior mutability without any moving. In the following code, SimpleNUnPin impls !Unpin so we can't move it. However, we can change its field a, this operator does not change address of the SimpleNUnPin instance.
struct SimpleNUnPin {
a: u64,
}
impl !Unpin for SimpleNUnPin {}
fn main()
{
let x = SimpleNUnPin { a: 1 };
pin_utils::pin_mut!(x);
x.as_mut().a = 2;
}
I want to find a way that enable interior mutability in Pin. I found pin-cell crate, which claims to be safer than RefCell. However, is that what I want? I find no example of it except from some API documentations. I tried to write some codes, but they can't compile.
use std::borrow::BorrowMut;
use pin_cell;
use pin_cell::{PinCell, PinMut};
fn main() {
let x = SimpleNUnPin { a: 1 };
let ptr1 = &x as *const _ as isize;
let xp = PinCell::new(x);
let mut b = xp.borrow_mut();
b.a = 2;
unsafe {
let n1 = &*(ptr1 as *const SimpleNUnPin);
assert_eq!(n1.a, 2);
}
The above code prints out error "error[E0609]: no field a on type &mut PinCell<SimpleNUnPin>". It is quite strange since my clion think it is a PinMut<SimpleNUnpin>.
Is there anyone can help? There are two questions:
Is pin-cell suitable in my case?
Why my code cam't compile, and has a contradict type dedution with clion?
I'm writing a function that takes a reference to an integer and returns a vector of that integer times 2, 5 times. I think that'd look something like:
fn foo(x: &i64) -> Vec<&i64> {
let mut v = vec![];
for i in 0..5 {
let q = x * 2;
v.push(&q);
}
v
}
fn main() {
let x = 5;
let q = foo(&x);
println!("{:?}", q);
}
The borrow checker goes nuts because I define a new variable, it's allocated on the stack, and goes out of scope at the end of the function.
What do I do? Certainly I can't go through life without writing functions that create new data! I'm aware there's Box, and Copy-type workarounds, but I'm interested in an idiomatic Rust solution.
I realize I could return a Vec<i64> but I think that'd run into the same issues? Mainly trying to come up with an "emblematic" problem for the general issue :)
EDIT: I only just realized that you wrote "I'm aware there's Box, Copy etc type workaround but I'm mostly interested in an idiomatic rust solution", but I've already typed the whole answer. :P And the solutions below are idiomatic Rust, this is all just how memory works! Don't go trying to return pointers to stack-allocated data in C or C++, because even if the compiler doesn't stop you, that doesn't mean anything good will come of it. ;)
Any time that you return a reference, that reference must have been a parameter to the function. In other words, if you're returning references to data, all that data must have been allocated outside of the function. You seem to understand this, I just want to make sure it's clear. :)
There are many potential ways of solving this problem depending on what your use case is.
In this particular example, because you don't need x for anything afterward, you can just give ownership to foo without bothering with references at all:
fn foo(x: i64) -> Vec<i64> {
std::iter::repeat(x * 2).take(5).collect()
}
fn main() {
let x = 5;
println!("{:?}", foo(x));
}
But let's say that you don't want to pass ownership into foo. You could still return a vector of references as long as you didn't want to mutate the underlying value:
fn foo(x: &i64) -> Vec<&i64> {
std::iter::repeat(x).take(5).collect()
}
fn main() {
let x = 5;
println!("{:?}", foo(&x));
}
...and likewise you could mutate the underlying value as long as you didn't want to hand out new pointers to it:
fn foo(x: &mut i64) -> &mut i64 {
*x *= 2;
x
}
fn main() {
let mut x = 5;
println!("{:?}", foo(&mut x));
}
...but of course, you want to do both. So if you're allocating memory and you want to return it, then you need to do it somewhere other than the stack. One thing you can do is just stuff it on the heap, using Box:
// Just for illustration, see the next example for a better approach
fn foo(x: &i64) -> Vec<Box<i64>> {
std::iter::repeat(Box::new(x * 2)).take(5).collect()
}
fn main() {
let x = 5;
println!("{:?}", foo(&x));
}
...though with the above I just want to make sure you're aware of Box as a general means of using the heap. Truthfully, simply using a Vec means that your data will be placed on the heap, so this works:
fn foo(x: &i64) -> Vec<i64> {
std::iter::repeat(x * 2).take(5).collect()
}
fn main() {
let x = 5;
println!("{:?}", foo(&x));
}
The above is probably the most idiomatic example here, though as ever your use case might demand something different.
Alternatively, you could pull a trick from C's playbook and pre-allocate the memory outside of foo, and then pass in a reference to it:
fn foo(x: &i64, v: &mut [i64; 5]) {
for i in v {
*i = x * 2;
}
}
fn main() {
let x = 5;
let mut v = [0; 5]; // fixed-size array on the stack
foo(&x, &mut v);
println!("{:?}", v);
}
Finally, if the function must take a reference as its parameter and you must mutate the referenced data and you must copy the reference itself and you must return these copied references, then you can use Cell for this:
use std::cell::Cell;
fn foo(x: &Cell<i64>) -> Vec<&Cell<i64>> {
x.set(x.get() * 2);
std::iter::repeat(x).take(5).collect()
}
fn main() {
let x = Cell::new(5);
println!("{:?}", foo(&x));
}
Cell is both efficient and non-surprising, though note that Cell works only on types that implement the Copy trait (which all the primitive numeric types do). If your type doesn't implement Copy then you can still do this same thing with RefCell, but it imposes a slight runtime overhead and opens up the possibilities for panics at runtime if you get the "borrowing" wrong.
I was under the impression that mutable references (i.e. &mut T) are always moved. That makes perfect sense, since they allow exclusive mutable access.
In the following piece of code I assign a mutable reference to another mutable reference and the original is moved. As a result I cannot use the original any more:
let mut value = 900;
let r_original = &mut value;
let r_new = r_original;
*r_original; // error: use of moved value *r_original
If I have a function like this:
fn make_move(_: &mut i32) {
}
and modify my original example to look like this:
let mut value = 900;
let r_original = &mut value;
make_move(r_original);
*r_original; // no complain
I would expect that the mutable reference r_original is moved when I call the function make_move with it. However that does not happen. I am still able to use the reference after the call.
If I use a generic function make_move_gen:
fn make_move_gen<T>(_: T) {
}
and call it like this:
let mut value = 900;
let r_original = &mut value;
make_move_gen(r_original);
*r_original; // error: use of moved value *r_original
The reference is moved again and therefore the program behaves as I would expect.
Why is the reference not moved when calling the function make_move?
Code example
There might actually be a good reason for this.
&mut T isn't actually a type: all borrows are parametrized by some (potentially inexpressible) lifetime.
When one writes
fn move_try(val: &mut ()) {
{ let new = val; }
*val
}
fn main() {
move_try(&mut ());
}
the type inference engine infers typeof new == typeof val, so they share the original lifetime. This means the borrow from new does not end until the borrow from val does.
This means it's equivalent to
fn move_try<'a>(val: &'a mut ()) {
{ let new: &'a mut _ = val; }
*val
}
fn main() {
move_try(&mut ());
}
However, when you write
fn move_try(val: &mut ()) {
{ let new: &mut _ = val; }
*val
}
fn main() {
move_try(&mut ());
}
a cast happens - the same kind of thing that lets you cast away pointer mutability. This means that the lifetime is some (seemingly unspecifiable) 'b < 'a. This involves a cast, and thus a reborrow, and so the reborrow is able to fall out of scope.
An always-reborrow rule would probably be nicer, but explicit declaration isn't too problematic.
I asked something along those lines here.
It seems that in some (many?) cases, instead of a move, a re-borrow takes place. Memory safety is not violated, only the "moved" value is still around. I could not find any docs on that behavior either.
#Levans opened a github issue here, although I'm not entirely convinced this is just a doc issue: dependably moving out of a &mut reference seems central to Rust's approach of ownership.
It's implicit reborrow. It's a topic not well documented.
This question has already been answered pretty well:
how implicit reborrow works
how reborrow works along with borrow split
If I tweak the generic one a bit, it would not complain either
fn make_move_gen<T>(_: &mut T) {
}
or
let _ = *r_original;