Automatic Borrowing in Function Signatures - rust

I'd like to understand, what is the correct way to write function signatures that do not need moved values, and are happy to accept a borrowed value. Consider:
fn calculate(x:&f32) -> bool {
if x > 5.0 {
true
}
false
}
Given that I don't consume the value, the borrowed value is good enough for me in this function.
Now when I call this function, the situation varies, either that is the terminal place for the x value to end up in:
let x = 4.0;
calculate(&x);
Or I need to use x again afterwards.
let x = 4.0;
calculate(&x);
let y = x + 5.0;
While the above approach works, it's a bit fiddly for the user of the calculate function to have to keep putting ampersands in, especially in cases like this where the number is hard-coded:
calculate(&-2.5);
Just looks absurd - I'm telling the computer to create a value, borrow it, use it, then dispose of it.
I'd expect that for sized types like floats there'd be a way to simplify this. I do understand that for dynamically sized values there might be further complications.
Ideally I'd like to be able to call the function and pass it either a borrowed value or an owned value.
What is the right way of putting this function signature together? Is there a way to accept both f32 and &f32?
Edit:
Additionally, the other situation I have is this:
let x_borrowed = &4.0;
calculate(x_borrowed);
As in, I only have the borrowed value.
From the comments made so far, it seems like the calculate function should instead accept an f32. And when calling it, I should give it either f32 or dereference an &f32. Unless there's an automatic dereferencing feature that someone can point me to?

For types that implement the Copy trait, there is no need to pass a reference as the function will not take ownership of the original value, instead creating a copy to be passed into the function.
However, with types that do not implement Copy (such as String), you can use the AsRef trait for things like this.
fn is_length_of_4(s: impl AsRef<str>) -> bool {
s.as_ref().len() == 4
}
// These will all work
assert!(is_length_of_4("Test"));
assert!(is_length_of_4(String::from("Test")));
assert!(is_length_of_4(&String::from("Test")));

Related

Use of moved value

I have a simple function in Rust that iterates through numbers and adds them to a vector if they fulfill a condition. This condition is a function that uses a previously defined variable, prime_factors.
The is_multiperfect function only needs to look things up in the prime_factors variable.
fn get_all_mpn_below(integer: usize) -> Vec<usize> {
let prime_factors = get_prime_factors_below(integer);
let mut mpn = vec![1];
for n in (2..integer).step_by(2) {
if is_multiperfect(n, prime_factors) {
mpn.push(n);
}
}
return mpn;
}
However, this yields the following error:
use of moved value: `prime_factors`
let prime_factors = get_prime_factors_below(integer);
------------- move occurs because `prime_factors` has type `HashMap<usize, Vec<usize>>`, which does not implement the `Copy` trait
if is_multiperfect(n, prime_factors) {
^^^^^^^^^^^^^ value moved here, in previous iteration of loop
I've looked up the error and found it was about ownership, however I fail to understand how ownership applies here.
How can I fix this error?
as I don't declare another variable.
Why would you think that's relevant?
Moving is simply the default behaviour of Rust when transferring values (whether setting them, or passing them to function, or returning them from functions). This occurs for all types which are not Copy.
How can I fix this error?
Hard to say since the problem is is_multiperfect and you don't provide that code, so the reader, not being psychic, has no way to know what is_multiperfect wants out of prime_factors.
Possible solutions are:
clone() the map, this creates a complete copy which the callee can use however it wants, leaving the original available, this gives the callee complete freedom but incurs a large cost for the caller
pass the map as an &mut (unique / mutable reference), if the callee needs to update it
pass the map as an & (shared reference), if the callee just needs to look things up in the map

Ergonomically passing a slice of trait objects

I am converting a variety of types to String when they are passed to a function. I'm not concerned about performance as much as ergonomics, so I want the conversion to be implicit. The original, less generic implementation of the function simply used &[impl Into<String>], but I think that it should be possible to pass a variety of types at once without manually converting each to a string.
The key is that ideally, all of the following cases should be valid calls to my function:
// String literals
perform_tasks(&["Hello", "world"]);
// Owned strings
perform_tasks(&[String::from("foo"), String::from("bar")]);
// Non-string types
perform_tasks(&[1,2,3]);
// A mix of any of them
perform_tasks(&["All", 3, String::from("types!")]);
Some various signatures I've attempted to use:
fn perform_tasks(items: &[impl Into<String>])
The original version fails twice; it can't handle numeric types without manual conversion, and it requires all of the arguments to be the same type.
fn perform_tasks(items: &[impl ToString])
This is slightly closer, but it still requires all of the arguments to be of one type.
fn perform_tasks(items: &[&dyn ToString])
Doing it this way is almost enough, but it won't compile unless I manually add a borrow on each argument.
And that's where we are. I suspect that either Borrow or AsRef will be involved in a solution, but I haven't found a way to get them to handle this situation. For convenience, here is a playground link to the final signature in use (without the needed references for it to compile), alongside the various tests.
The following way works for the first three cases if I understand your intention correctly.
pub fn perform_tasks<I, A>(values: I) -> Vec<String>
where
A: ToString,
I: IntoIterator<Item = A>,
{
values.into_iter().map(|s| s.to_string()).collect()
}
As the other comments pointed out, Rust does not support an array of mixed types. However, you can do one extra step to convert them into a &[&dyn fmt::Display] and then call the same function perform_tasks to get their strings.
let slice: &[&dyn std::fmt::Display] = &[&"All", &3, &String::from("types!")];
perform_tasks(slice);
Here is the playground.
If I understand your intention right, what you want is like this
fn main() {
let a = 1;
myfn(a);
}
fn myfn(i: &dyn SomeTrait) {
//do something
}
So it's like implicitly borrow an object as function argument. However, Rust won't let you to implicitly borrow some objects since borrowing is quite an important safety measure in rust and & can help other programmers quickly identified which is a reference and which is not. Thus Rust is designed to enforce the & to avoid confusion.

How can I swap out the value of a mutable reference, temporarily taking ownership? [duplicate]

This question already has answers here:
Temporarily move out of borrowed content
(3 answers)
Closed 1 year ago.
I have a function that takes ownership of some data, modifies it destructively, and returns it.
fn transform(s: MyData) -> MyData {
todo!()
}
In some situations, I have a &mut MyData reference. I would like to apply transform to &mut MyData.
fn transform_mut(data_ref: &mut MyData) {
*data_ref = transform(*data_ref);
}
Rust Playground
However, this causes a compiler error.
error[E0507]: cannot move out of `*data_ref` which is behind a mutable reference
--> src/lib.rs:10:27
|
10 | *data_ref = transform(*data_ref);
| ^^^^^^^^^ move occurs because `*data_ref` has type `MyData`, which does not implement the `Copy` trait
I considered using mem::swap and mem::replace, but they require that you already have some valid value to put into the reference before taking another one out.
Is there any way to accomplish this? MyData doesn't have a sensible default or dummy value to temporarily stash in the reference. It feels like because I have exclusive access the owner shouldn't care about the transformation, but my intuition might be wrong here.
It feels like because I have exclusive access the owner shouldn't care about the transformation, but my intuition might be wrong here.
The problem with this idea is that if this were allowed, and the function transform panicked, there is no longer a valid value in *data_ref, which is visible if the unwind is caught or inside of Drop handling for the memory data_ref points into.
For example, let's implement this in the obvious fashion, by just copying the data out of the referent and back in:
use std::ptr;
fn naive_modify_in_place<T>(place: &mut T, f: fn(T) -> T) {
let mut value = unsafe { ptr::read(place) };
value = f(value);
unsafe { ptr::write(place, value) };
}
fn main() {
let mut x = Box::new(1234);
naive_modify_in_place(&mut x, |x| panic!("oops"));
}
If you run this program (Rust Playground link), it will crash with a “double free” error. This is because unwinding from the panicking function dropped its argument, and then unwinding from main dropped x — which is the same box, already deallocated.
There are a couple of crates designed specifically to solve this problem:
take_mut, and replace_with intended to improve on it. (I haven't used either, particularly.) Both of these offer two options for dealing with panics:
Force an abort (program immediately exits with no ability to handle the panic or clean up anything else).
Replace the referent of the reference with a different freshly computed value, since the previous one was lost when the panic started.
Of course, if you have no valid alternative value then you can't take option 2. In that case, you might want to consider bypassing this situation entirely by adding a placeholder: if you can store an Option<MyData> and pass an &mut Option<MyData>, then your code can use Option::take to temporarily remove the value and leave None in its place. The None will only ever be visible if there was a panic, and if your code isn't catching panics then it will never matter. But it does mean that every access to the data requires retrieving it from the Option (e.g. using .as_ref().unwrap()).
You could derive (or impl) Clone for MyData, then pass the clone into your destructive transform.
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e3a4d737a700ef6baa35160545f0e514
Cloning removes the problem of the data being behind a reference.

A cell with interior mutability allowing arbitrary mutation actions

Standard Cell struct provides interior mutability but allows only a few mutation methods such as set(), swap() and replace(). All of these methods change the whole content of the Cell.
However, sometimes more specific manipulations are needed, for example, to change only a part of data contained in the Cell.
So I tried to implement some kind of universal Cell, allowing arbitrary data manipulation.
The manipulation is represented by user-defined closure that accepts a single argument - &mut reference to the interior data of the Cell, so the user itself can deside what to do with the Cell interior. The code below demonstrates the idea:
use std::cell::UnsafeCell;
struct MtCell<Data>{
dcell: UnsafeCell<Data>,
}
impl<Data> MtCell<Data>{
fn new(d: Data) -> MtCell<Data> {
return MtCell{dcell: UnsafeCell::new(d)};
}
fn exec<F, RetType>(&self, func: F) -> RetType where
RetType: Copy,
F: Fn(&mut Data) -> RetType
{
let p = self.dcell.get();
let pd: &mut Data;
unsafe{ pd = &mut *p; }
return func(pd);
}
}
// test:
type MyCell = MtCell<usize>;
fn main(){
let c: MyCell = MyCell::new(5);
println!("initial state: {}", c.exec(|pd| {return *pd;}));
println!("state changed to {}", c.exec(|pd| {
*pd += 10; // modify the interior "in place"
return *pd;
}));
}
However, I have some concerns regarding the code.
Is it safe, i.e can some safe but malicious closure break Rust mutability/borrowing/lifetime rules by using this "universal" cell?
I consider it safe since lifetime of the interior reference parameter prohibits its exposition beyond the closure call time. But I still have doubts (I'm new to Rust).
Maybe I'm re-inventing the wheel and there exist some templates or techniques solving the problem?
Note: I posted the question here (not on code review) as it seems more related to the language rather than code itself (which represents just a concept).
[EDIT] I'd want zero cost abstraction without possibility of runtime failures, so RefCell is not perfect solution.
This is a very common pitfall for Rust beginners.
Is it safe, i.e can some safe but malicious closure break Rust mutability/borrowing/lifetime rules by using this "universal" cell? I consider it safe since lifetime of the interior reference parameter prohibits its exposition beyond the closure call time. But I still have doubts (I'm new to Rust).
In a word, no.
Playground
fn main() {
let mt_cell = MtCell::new(123i8);
mt_cell.exec(|ref1: &mut i8| {
mt_cell.exec(|ref2: &mut i8| {
println!("Double mutable ref!: {:?} {:?}", ref1, ref2);
})
})
}
You're absolutely right that the reference cannot be used outside of the closure, but inside the closure, all bets are off! In fact, pretty much any operation (read or write) on the cell within the closure is undefined behavior (UB), and may cause corruption/crashes anywhere in your program.
Maybe I'm re-inventing the wheel and there exist some templates or techniques solving the problem?
Using Cell is often not the best technique, but it's impossible to know what the best solution is without knowing more about the problem.
If you insist on Cell, there are safe ways to do this. The unstable (ie. beta) Cell::update() method is literally implemented with the following code (when T: Copy):
pub fn update<F>(&self, f: F) -> T
where
F: FnOnce(T) -> T,
{
let old = self.get();
let new = f(old);
self.set(new);
new
}
Or you could use Cell::get_mut(), but I guess that defeats the whole purpose of Cell.
However, usually the best way to change only part of a Cell is by breaking it up into separate Cells. For example, instead of Cell<(i8, i8, i8)>, use (Cell<i8>, Cell<i8>, Cell<i8>).
Still, IMO, Cell is rarely the best solution. Interior mutability is a common design in C and many other languages, but it is somewhat more rare in Rust, at least via shared references and Cell, for a number of reasons (e.g. it's not Sync, and in general people don't expect interior mutability without &mut). Ask yourself why you are using Cell and if it is really impossible to reorganize your code to use normal &mut references.
IMO the bottom line is actually about safety: if no matter what you do, the compiler complains and it seems that you need to use unsafe, then I guarantee you that 99% of the time either:
There's a safe (but possibly complex/unintuitive) way to do it, or
It's actually undefined behavior (like in this case).
EDIT: Frxstrem's answer also has better info about when to use Cell/RefCell.
Your code is not safe, since you can call c.exec inside c.exec to get two mutable references to the cell contents, as demonstrated by this snippet containing only safe code:
let c: MyCell = MyCell::new(5);
c.exec(|n| {
// need `RefCell` to access mutable reference from within `Fn` closure
let n = RefCell::new(n);
c.exec(|m| {
let n = &mut *n.borrow_mut();
// now `n` and `m` are mutable references to the same data, despite using
// no unsafe code. this is BAD!
})
})
In fact, this is exactly the reason why we have both Cell and RefCell:
Cell only allows you to get and set a value and does not allow you to get a mutable reference from an immutable one (thus avoiding the above issue), but it does not have any runtime cost.
RefCell allows you to get a mutable reference from an immutable one, but needs to perform checks at runtime to ensure that this is safe.
As far as I know, there's not really any safe way around this, so you need to make a choice in your code between no runtime cost but less flexibility, and more flexibility but with a small runtime cost.

How to force a move of a type which implements the Copy trait?

A custom type by default is moved through default assignment. By implementing the Copy trait, I get "shallow copy semantics" through default assignment. I may also get "deep copy semantics" by implementing the Clone trait.
Is there a way to force a move on a Copy type?
I tried using the move keyword and a closure (let new_id = move || id;) but I get an error message. I'm not into closures yet, but, from seeing them here and there, I thought that that would have worked.
I don't really understand your question, but you certainly seem confused. So I'll address what seems to be the root of this confusion:
The C++ notions of copy/move I think I get correctly, but this 'everything is a memcpy anyway' is, well, it hasn't been very intuitive any time I read it
When thinking about Rust's move semantics, ignore C++. The C++ story is way more complicated than Rust's, which is remarkably simple. However, explaining Rust's semantics in terms of C++ is a mess.
TL;DR: Copies are moves. Moves are copies. Only the type checker knows the difference. So when you want to "force a move" for a Copy type, you are asking for something you already have.
So we have three semantics:
let a = b where b is not Copy
let a = b where b is Copy
let a = b.clone() where b is Clone
Note: There is no meaningful difference between assignment and initialization (like in C++) - assignment just first drops the old value.
Note: Function call arguments work just like assignment. f(b) assigns b to the argument of f.
First things first.
The a = b always performs a memcpy.
This is true in all three cases.
When you do let a = b, b is memcpy'd into a.
When you do let a = b.clone(), the result of b.clone() is memcpy'd into a.
Moves
Imagine b was a Vec. A Vec looks like this:
{ &mut data, length, capacity }
When you write let a = b you thus end up with:
b = { &mut data, length, capacity }
a = { &mut data, length, capacity }
This means that a and b both reference &mut data, which means we have aliased mutable data.
The type-system doesn't like this so says we can't use b again. Any access to b will fail at compile-time.
Note: a and b don't have to alias heap data to make using both a bad idea. For example, they could both be file handles - a copy would result in the file being closed twice.
Note: Moves do have extra semantics when destructors are involved, but the compiler won't let you write Copy on types with destructors anyway.
Copies
Imagine b was an Option<i32>. An Option<i32> looks like this:
{ is_valid, data }
When you write let a = b you thus end up with:
b = { is_valid, data }
a = { is_valid, data }
These are both usable simultaneously. To tell the type system that this is the case, one marks Option<i32> as Copy.
Note: Marking something copy doesn't change what the code does. It only allows more code. If you remove a Copy implementation, your code will either error or do exactly the same thing. In the same vein, marking a non-Copy type as Copy will not change any compiled code.
Clones
Imagine you want to copy a Vec, then. You implement Clone, which produces a new Vec, and do
let a = b.clone()
This performs two steps. We start with:
b = { &mut data, length, capacity }
Running b.clone() gives us an additional rvalue temporary
b = { &mut data, length, capacity }
{ &mut copy, length, capacity } // temporary
Running let a = b.clone() memcpys this into a:
b = { &mut data, length, capacity }
{ &mut copy, length, capacity } // temporary
a = { &mut copy, length, capacity }
Further access of the temporary is thus prevented by the type system, since Vec is not Copy.
But what about efficiency?
One thing I skipped over so far is that moves and copies can be elided. Rust guarantees certain trivial moves and copies to be elided.
Because the compiler (after lifetime checking) sees the same result in both cases, these are elided in exactly the same way.
Wrap the copyable type in another type that doesn't implement Copy.
struct Noncopyable<T>(T);
fn main() {
let v0 = Noncopyable(1);
let v1 = v0;
println!("{}", v0.0); // error: use of moved value: `v0.0`
}
New Answer
Sometimes I just want it to scream at me "put a new value in here!".
Then the answer is "no". When moving a type that implements Copy, both the source and destination will always be valid. When moving a type that does not implement Copy, the source will never be valid and the destination will always be valid. There is no syntax or trait that means "let me pick if this type that implements Copy acts as Copy at this time".
Original Answer
I just want to sometimes say "yeah, this type is Copy, but I really don't need this value in this variable anymore. This function takes an arg by val, just take it."
It sounds like you are trying to do the job of the optimizer by hand. Don't worry about that, the optimizer will do that for you. This has the benefit of not needing to worry about it.
Moves and copies are basically just the same runtime operation under the covers. The compiler inserts code to make a bitwise copy from the first variable's address into the second variable's address. In the case of a move, the compiler also invalidates the first variable so that if it subsequently used it will be a compile error.
Even so, I think there would be still be validity if Rust language allowed a program to say the assignment was an explicit move instead of a copy. It could catch bugs by preventing inadvertant references to the wrong instance. It might also generate more efficient code in some instances if the compiler knows you don't need two copies and could jiggle the bindings around to avoid the bitwise copy.
e.g. if you could state a = move assignment or similar.
let coord = (99.9, 73.45);
let mut coord2 = move coord;
coord2.0 += 100.0;
println!("coord2 = {:?}", coord2);
println!("coord = {:?}", coord); // Error
At runtime, copies and moves, in Rust, have the same effect. However, at compile-time, in the case of a move, the variable which an object is moved from is marked as unusable, but not in the case of a copy.
When you're using Copy types, you always want value semantics, and object semantics when not using Copy types.
Objects, in Rust, don't have a consistent address: the addresses often change between moves because of the runtime behavior, i.e. they are owned by exactly one binding. This is very different from other languages!
In Rust when you use (or move, in Rust's terms) a value that is Copy, the original value is still valid. If you want to simulate the case that like other non-copyable values, to invalidate after a specific use, you can do:
let v = 42i32;
// ...
let m = v;
// redefine v such that v is no longer a valid (initialized) variable afterwards
// Unfortunately you have to write a type here. () is the easiest,
// but can be used unintentionally.
let v: ();
// If the ! type was stabilized, you can write
let v: !;
// otherwise, you can define your own:
enum NeverType {};
let v: NeverType;
// ...
If you later change v to something that is not Copy, you don't have to change the code above to avoid using the moved value.
Correction on some misunderstanding on the question
The difference between Clone and Copy is NOT "shallow copy" and "deep copy" semantics. Copy is "memcpy" semantics and Clone is whatever the implementors like, that is the only difference. Although, by definition, things which require a "deep copy" are not able to implement Copy.
When a type implements both Copy and Clone, it is expected that both have the same semantics except that Clone can have side effects. For a type that implements Copy, its Clone should not have "deep copy" semantics and the cloned result is expected to be the same as a copied result.
As an attempt, if you want to use the closure to help, you probably wanted to run the closure, like let new_id = (move || id)();. If id is copy then id is still valid after the move, so this does not help, at all.

Resources