I am working with some code that uses the libgit2 bindings for Rust to perform operations on a Git repository.
I have a section of code that transforms a "committish" reference (something that either is a commit or ultimately references a commit, like a tag) that currently looks like this:
let mut target_commit = target_object
.peel(git2::ObjectType::Commit)
.map_err(|_| anyhow!("Target `{commitish}` cannot be evaluated as a commit"))?
.into_commit()
.map_err(|_| anyhow!("Target `{commitish}` cannot be evaluated as a commit"))?;
I'd like to avoid having two identical calls to map_err in that
chain. Based on the description of the and_then combinator...
Calls op if the result is Ok, otherwise returns the Err value of self.
...I thought maybe this would work:
let mut target_commit = target_object
.peel(git2::ObjectType::Commit)
.and_then(|c| c.into_commit())
.map_err(|_| anyhow!("Target `{commitish}` cannot be evaluated as a commit"))?;
But that fails with:
106 | .and_then(|c| c.into_commit())
| ^^^^^^^^^^^^^^^ expected struct `git2::Error`, found struct `git2::Object`
What's the correct way to simplify this expression?
Simplification could mean multiple things. One way to simplify is to remove the duplication of the anyhow!() invocation.
Since you don't actually use the error information, you can use Result::ok to discard the error information by converting the Result<T, E> to Option<T>, then use Option::ok_or_else at the end of a chain to turn the final Option back into a Result.
let mut target_commit = target_object
.peel(git2::ObjectType::Commit).ok()
.and_then(|c| c.into_commit().ok())
.ok_or_else(|| anyhow!("Target `{commitish}` cannot be evaluated as a commit"))?;
Related
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
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.
I have been playing with Rust by porting my Score4 AI engine to it - basing the work on my functional-style implementation in OCaml. I specifically wanted to see how Rust fares with functional-style code.
The end result: It works, and it's very fast - much faster than OCaml. It almost touches the speed of imperative-style C/C++ - which is really cool.
There's a thing that troubles me, though — why do I need two ampersands in the last line of this code?
let moves_and_scores: Vec<_> = moves_and_boards
.iter()
.map(|&(column,board)| (column, score_board(&board)))
.collect();
let target_score = if maximize_or_minimize {
ORANGE_WINS
} else {
YELLOW_WINS
};
if let Some(killer_move) = moves_and_scores.iter()
.find(|& &(_,score)| score==target_score) {
...
I added them is because the compiler errors "guided" me to it; but I am trying to understand why... I used the trick mentioned elsewhere in Stack Overflow to "ask" the compiler to tell me what type something is:
let moves_and_scores: Vec<_> = moves_and_boards
.iter()
.map(|&(column,board)| (column, score_board(&board)))
.collect();
let () = moves_and_scores;
...which caused this error:
src/main.rs:108:9: 108:11 error: mismatched types:
expected `collections::vec::Vec<(u32, i32)>`,
found `()`
(expected struct `collections::vec::Vec`,
found ()) [E0308]
src/main.rs:108 let () = moves_and_scores;
...as I expected, moves_and_scores is a vector of tuples: Vec<(u32, i32)>. But then, in the immediate next line, iter() and find() force me to use the hideous double ampersands in the closure parameter:
if let Some(killer_move) = moves_and_scores.iter()
.find(|& &(_,score)| score==target_score) {
Why does the find closure need two ampersands? I could see why it may need one (pass the tuple by reference to save time/space) but why two? Is it because of the iter? That is, is the iter creating references, and then find expects a reference on each input, so a reference on a reference?
If this is so, isn't this, arguably, a rather ugly design flaw in Rust?
In fact, I would expect find and map and all the rest of the functional primitives to be parts of the collections themselves. Forcing me to iter() to do any kind of functional-style work seems burdensome, and even more so if it forces this kind of "double ampersands" in every possible functional chain.
I am hoping I am missing something obvious - any help/clarification most welcome.
This here
moves_and_scores.iter()
gives you an iterator over borrowed vector elements. If you follow the API doc what type this is, you'll notice that it's just the iterator for a borrowed slice and this implements Iterator with Item=&T where T is (u32, i32) in your case.
Then, you use find which takes a predicate which takes a &Item as parameter. Sice Item already is a reference in your case, the predicate has to take a &&(u32, i32).
pub trait Iterator {
...
fn find<P>(&mut self, predicate: P) -> Option<Self::Item>
where P: FnMut(&Self::Item) -> bool {...}
... ^
It was probably defined like this because it's only supposed to inspect the item and return a bool. This does not require the item being passed by value.
If you want an iterator over (u32, i32) you could write
moves_and_scores.iter().cloned()
cloned() converts the iterator from one with an Item type &T to one with an Item type T if T is Clone. Another way to do it would be to use into_iter() instead of iter().
moves_and_scores.into_iter()
The difference between the two is that the first option clones the borrowed elements while the 2nd one consumes the vector and moves the elements out of it.
By writing the lambda like this
|&&(_, score)| score == target_score
you destructure the "double reference" and create a local copy of the i32. This is allowed since i32 is a simple type that is Copy.
Instead of destructuring the parameter of your predicate you could also write
|move_and_score| move_and_score.1 == target_score
because the dot operator automatically dereferences as many times as needed.
I'm starting to learn Rust and I tried to implement a function to reverse a vector of strings. I found a solution but I don't understand why it works.
This works:
fn reverse_strings(strings:Vec<&str>) -> Vec<&str> {
let actual: Vec<_> = strings.iter().cloned().rev().collect();
return actual;
}
But this doesn't.
fn reverse_strings(strings:Vec<&str>) -> Vec<&str> {
let actual: Vec<_> = strings.iter().rev().collect(); // without clone
return actual;
}
Error message
src/main.rs:28:10: 28:16 error: mismatched types:
expected `collections::vec::Vec<&str>`,
found `collections::vec::Vec<&&str>`
(expected str,
found &-ptr) [E0308]
Can someone explain to me why? What happens in the second function? Thanks!
So the call to .cloned() is essentially like doing .map(|i| i.clone()) in the same position (i.e. you can replace the former with the latter).
The thing is that when you call iter(), you're iterating/operating on references to the items being iterated. Notice that the vector already consists of 'references', specifically string slices.
So to zoom in a bit, let's replace cloned() with the equivalent map() that I mentioned above, for pedagogical purposes, since they are equivalent. This is what it actually looks like:
.map(|i: & &str| i.clone())
So notice that that's a reference to a reference (slice), because like I said, iter() operates on references to the items, not the items themselves. So since a single element in the vector being iterated is of type &str, then we're actually getting a reference to that, i.e. & &str. By calling clone() on each of these items, we go from a & &str to a &str, just like calling .clone() on a &i64 would result in an i64.
So to bring everything together, iter() iterates over references to the elements. So if you create a new vector from the collected items yielded by the iterator you construct (which you constructed by calling iter()) you would get a vector of references to references, that is:
let actual: Vec<& &str> = strings.iter().rev().collect();
So first of all realize that this is not the same as the type you're saying the function returns, Vec<&str>. More fundamentally, however, the lifetimes of these references would be local to the function, so even if you changed the return type to Vec<& &str> you would get a lifetime error.
Something else you could do, however, is to use the into_iter() method. This method actually does iterate over each element, not a reference to it. However, this means that the elements are moved from the original iterator/container. This is only possible in your situation because you're passing the vector by value, so you're allowed to move elements out of it.
fn reverse_strings(strings:Vec<&str>) -> Vec<&str> {
let actual: Vec<_> = strings.into_iter().rev().collect();
return actual;
}
playpen
This probably makes a bit more sense than cloning, since we are passed the vector by value, we're allowed to do anything with the elements, including moving them to a different location (in this case the new, reversed vector). And even if we don't, the vector will be dropped at the end of that function anyways, so we might as well. Cloning would be more appropriate if we're not allowed to do that (e.g. if we were passed the vector by reference, or a slice instead of a vector more likely).
This is for the current 0.6 Rust trunk by the way, not sure the exact commit.
Let's say I want to for each over some strings, and my closure takes a borrowed string pointer argument (&str). I want my closure to add its argument to an owned vector of owned strings ~[~str] to be returned. My understanding of Rust is weak, but I think that strings are a special case where you can't dereference them with * right? How do I get my strings from &str into the vector's push method which takes a ~str?
Here's some code that doesn't compile
fn read_all_lines() -> ~[~str] {
let mut result = ~[];
let reader = io::stdin();
let util = #reader as #io::ReaderUtil;
for util.each_line |line| {
result.push(line);
}
result
}
It doesn't compile because it's inferring result's type to be [&str] since that's what I'm pushing onto it. Not to mention its lifetime will be wrong since I'm adding a shorter-lived variable to it.
I realize I could use ReaderUtil's read_line() method which returns a ~str. But this is just an example.
So, how do I get an owned string from a borrowed string? Or am I totally misunderstanding.
You should call the StrSlice trait's method, to_owned, as in:
fn read_all_lines() -> ~[~str] {
let mut result = ~[];
let reader = io::stdin();
let util = #reader as #io::ReaderUtil;
for util.each_line |line| {
result.push(line.to_owned());
}
result
}
StrSlice trait docs are here:
http://static.rust-lang.org/doc/core/str.html#trait-strslice
You can't.
For one, it doesn't work semantically: a ~str promises that only one thing owns it at a time. But a &str is borrowed, so what happens to the place you borrowed from? It has no way of knowing that you're trying to steal away its only reference, and it would be pretty rude to trash the caller's data out from under it besides.
For another, it doesn't work logically: ~-pointers and #-pointers are allocated in completely different heaps, and a & doesn't know which heap, so it can't be converted to ~ and still guarantee that the underlying data lives in the right place.
So you can either use read_line or make a copy, which I'm... not quite sure how to do :)
I do wonder why the API is like this, when & is the most restricted of the pointers. ~ should work just as well here; it's not like the iterated strings already exist somewhere else and need to be borrowed.
At first I thought it was possible to use copy line to create owning pointer from the borrowed pointer to the string but this apparently copies burrowed pointer.
So I found str::from_slice(s: &str) -> ~str. This is probably what you need.