Matching String: cannot move out of borrowed content - rust

req.url.fragment is an optional String. If it has a value, I want to copy that value into fragment, otherwise I want to assign an empty string. I keep getting the error that I cannot move out of borrowed content.
How do I resolve this?
fn fb_token(req: &mut Request) -> IronResult<Response> {
let fragment = match req.url.fragment {
Some(fragment) => fragment,
None => "".to_string(),
};
Ok(Response::with((status::Ok, fragment)))
}

It depends on what you want to do with the existing string in the structure.
let fragment = match req.url.fragment {
Some(fragment) => fragment,
None => "".to_string(),
};
In this code, you are moving the String out of req.url.fragment, but that would leave it in an undefined state. That's a bad thing, and Rust prevents you from doing that!
As the error message states:
to prevent the move, use ref fragment or ref mut fragment to capture value by reference
If you want to leave the string where it is and return a copy, then you can take a reference and then clone it:
let fragment = match req.url {
Some(ref fragment) => fragment.clone(),
None => "".to_string()
};
If you want to leave the existing string as a None, then you can use take:
let fragment = match req.url.take() {
Some(fragment) => fragment,
None => "".to_string()
};
Even shorter, you can use unwrap_or_else:
let fragment = req.url.take().unwrap_or_else(String::new);

The problem here is that one cannot invalidate a &mut reference, and moving ownership out is one way to invalidate. (There's a few links to similar questions/answers in the sidebar on the right.)
A fix is to instead use references, which should work quite well in this case:
fn fb_token(req: &mut Request) -> IronResult<Response> {
let fragment: &str = match req.url.fragment {
Some(ref fragment) => &**fragment,
None => "",
};
Ok(Response::with((status::Ok, fragment)))
}
(The &str annotation isn't necessary, just to make it clearer what's happening.)
This works because iron implements Modifier<Response> for &str, so that type can be used with with instead of the owned String.

Related

How do I borrow a value inside an `or_else` closure after it's been borrowed for a function call?

I'm making an asset library that's supposed to hold a tree of nodes. A node can be either an Asset or a Directory:
struct Node {
name: String,
data: Content,
}
enum Content {
Directory(Vec<Node>),
Asset(Rc<dyn Asset>, usize),
}
I have a create_dir_all method that's supposed to take a path and create all directories. The way I want to do that is to iterate over the components of the path, search for the corresponding node, and if it is found, move on to the next, if I get a NotFound error, create the node and then move on.
My first idea was to do this:
let mut current_node = &mut self.root;
for component in path.components() {
let name = component.as_os_str().to_str().unwrap();
current_node = current_node.get_child(name).or_else(|error| match error {
ErrorKind::NotFound => {
current_node.create_child(Node::directory(String::from(name)))
}
error => Err(error),
})?;
}
I believe I understand why that doesn't work: the closure has to borrow current_node mutably at the time it's created, so the create_child call is a second mutable borrow.
However, I can't seem to find any way around that. Here's another attempt:
let mut current_node = &mut self.root;
for component in path.components() {
let name = component.as_os_str().to_str().unwrap();
current_node = match current_node.get_child(name) {
Ok(child) => Ok(child),
Err(error) => match error {
ErrorKind::NotFound => {
current_node.create_child(Node::directory(String::from(name)))
}
error => Err(error),
},
}?;
}
Here's my understanding of this one:
The lifetime of current_node inside the inner match is the same one that's assigned on the get_child call, so the create_child call will be a second mutable borrow.
While researching this, I found that the lifetime of the result will be tied to the lifetime of current_node. If that's the case, is it impossible to match the error and generate a fallback value? That seems like a very simple thing to do but I cannot figure out what I'm missing.
I had posted this to the Rust forums as well and got a helpful response there: the borrow checker "does not recognize a borrow being continued on one arm of a match but terminated on another."
I went with the suggestion from that response and will look into writing some unsafe code here in the future. It looks like this:
let mut current_node = &mut self.root;
for component in path.components() {
let name = component.as_os_str().to_str().unwrap();
if let Err(error) = current_node.get_child(name) {
match error {
ErrorKind::NotFound => {
current_node.create_child(Node::directory(String::from(name)))
}
error => Err(error),
}?;
}
current_node = current_node.get_child(name)?;
}

Clean way to get Option::unwrap_or_else behaviour with an Option<&T>

I would like to know if there's any elegant solution for getting code/behaviour similar to unwrap_or_else on an Option<&T>. My use case is to pass an optional reference to a function and if it's not used then create a default value of the same type to use. Here's a boiled-down version of my code:
#[derive(Debug)]
struct ExpensiveUnclonableThing {}
fn make_the_thing() -> ExpensiveUnclonableThing {
// making the thing is slow
// ...
ExpensiveUnclonableThing {}
}
fn use_the_thing(thing_ref: &ExpensiveUnclonableThing) {
dbg!(thing_ref);
}
fn use_or_default(thing_ref_opt: Option<&ExpensiveUnclonableThing>) {
enum MaybeDefaultedRef<'a> {
Passed(&'a ExpensiveUnclonableThing),
Defaulted(ExpensiveUnclonableThing),
}
let thing_md = match thing_ref_opt {
Some(thing_ref) => MaybeDefaultedRef::Passed(thing_ref),
None => MaybeDefaultedRef::Defaulted(make_the_thing()),
};
let thing_ref = match &thing_md {
MaybeDefaultedRef::Passed(thing) => thing,
MaybeDefaultedRef::Defaulted(thing) => thing,
};
use_the_thing(thing_ref);
}
fn use_or_default_nicer(thing_ref_opt: Option<&ExpensiveUnclonableThing>) {
let thing_ref = thing_ref_opt.unwrap_or_else(|| &make_the_thing());
use_the_thing(thing_ref);
}
fn main() {
let thing = make_the_thing();
use_or_default(Some(&thing));
use_or_default(None);
use_or_default_nicer(Some(&thing));
use_or_default_nicer(None);
}
The thing is dropped right away when the unwrap_or_else closure ends, so I of course get an error stating that I can't do that:
error[E0515]: cannot return reference to temporary value
--> src/main.rs:31:53
|
31 | let thing_ref = thing_ref_opt.unwrap_or_else(|| &make_the_thing());
| ^----------------
| ||
| |temporary value created here
| returns a reference to data owned by the current function
What is the 'idiomatic Rust' way of writing use_or_default? Is there a way I can get it to look similar to how use_or_default_nicer is implemented other than by creating a generic MaybeDefaultedRef<T> type + with some convenience methods? I am open to refactoring the whole thing if there's a better way.
You can write something like this:
fn use_or_default_nicer(thing_ref_opt: Option<&ExpensiveUnclonableThing>) {
let mut maybe = None;
let thing_ref = thing_ref_opt.unwrap_or_else(
|| maybe.insert(make_the_thing())
);
use_the_thing(thing_ref);
}
That is, you can keep the value itself outside of the function and then assign to it if necessary. Unfortunately, an unitialized value cannot be capture by a lambda so you have to make the variable Option<ExpensiveUnclonableThing> and initialize with None.
But in a real code of mine, I had the same issue and I wrote a manual match:
fn use_or_default_nicer(thing_ref_opt: Option<&ExpensiveUnclonableThing>) {
let maybe;
let thing_ref = match thing_ref_opt {
Some(x) => x,
None => {
maybe = make_the_thing();
&maybe
}
};
use_the_thing(thing_ref);
}
In my opinion this is nicer even if a bit longer, because you don't need the Option<_> or the maybe variable being mutable` or the fake initialization.
Some people feel a bit of a defeat when they match on an Option, and think it is un-idiomatic, but I don't particularly care.
A plain old if/else would do also, no need to convolute things:
fn use_or_default_nicer(thing_ref_opt: Option<&ExpensiveUnclonableThing>) {
if let Some(e) = thing_ref_opt {
use_the_thing(e);
} else {
let e = make_the_thing();
use_the_thing(&e);
}
}
Playground

Why can't Rust find method for enum generated using proc_macro_attribute?

I am trying to write procedural macros that will accept a Rust enum like
#[repr(u8)]
enum Ty {
A,
B
}
and generate a method for the enum that will let me convert an u8 into an allowed variant like this
fn from_byte(byte: u8) -> Ty {
match {
0 => Ty::A,
1 => Ty::B,
_ => unreachable!()
}
}
This is what I have implemented using proc_macro lib. (no external lib)
#![feature(proc_macro_diagnostic)]
#![feature(proc_macro_quote)]
extern crate proc_macro;
use proc_macro::{TokenStream, Diagnostic, Level, TokenTree, Ident, Group, Literal};
use proc_macro::quote;
fn report_error(tt: TokenTree, msg: &str) {
Diagnostic::spanned(tt.span(), Level::Error, msg).emit();
}
fn variants_from_group(group: Group) -> Vec<Ident> {
let mut iter = group.stream().into_iter();
let mut res = vec![];
while let Some(TokenTree::Ident(id)) = iter.next() {
match iter.next() {
Some(TokenTree::Punct(_)) | None => res.push(id),
Some(tt) => {
report_error(tt, "unexpected variant. Only unit variants accepted.");
return res
}
}
}
res
}
#[proc_macro_attribute]
pub fn procmac(args: TokenStream, input: TokenStream) -> TokenStream {
let _ = args;
let mut res = TokenStream::new();
res.extend(input.clone());
let mut iter = input.into_iter()
.skip_while(|tt| if let TokenTree::Punct(_) | TokenTree::Group(_) = tt {true} else {false})
.skip_while(|tt| tt.to_string() == "pub");
match iter.next() {
Some(tt # TokenTree::Ident(_)) if tt.to_string() == "enum" => (),
Some(tt) => {
report_error(tt, "unexpected token. this should be only used with enums");
return res
},
None => return res
}
match iter.next() {
Some(tt) => {
let variants = match iter.next() {
Some(TokenTree::Group(g)) => {
variants_from_group(g)
}
_ => return res
};
let mut match_arms = TokenStream::new();
for (i, v) in variants.into_iter().enumerate() {
let lhs = TokenTree::Literal(Literal::u8_suffixed(i as u8));
if i >= u8::MAX as usize {
report_error(lhs, "enum can have only u8::MAX variants");
return res
}
let rhs = TokenTree::Ident(v);
match_arms.extend(quote! {
$lhs => $tt::$rhs,
})
}
res.extend(quote!(impl $tt {
pub fn from_byte(byte: u8) -> $tt {
match byte {
$match_arms
_ => unreachable!()
}
}
}))
}
_ => ()
}
res
}
And this is how I am using it.
use helper_macros::procmac;
#[procmac]
#[derive(Debug)]
#[repr(u8)]
enum Ty {
A,
B
}
fn main() {
println!("TEST - {:?}", Ty::from_byte(0))
}
The problem is this causing an error from the compiler. The exact error being
error[E0599]: no variant or associated item named `from_byte` found for enum `Ty` in the current scope
--> main/src/main.rs:91:32
|
85 | enum Ty {
| ------- variant or associated item `from_byte` not found here
...
91 | println!("TEST - {:?}", Ty::from_byte(0))
| ^^^^^^^^^ variant or associated item not found in `Ty`
Running cargo expand though generate the proper code. And running that code directly works as expected. And so I am stumped. It could be I am missing something about how proc_macros should be used since this is the first time I am playing with them and I don't see anything that would cause this error. I am following the sorted portion of the proc_macro_workshop0. Only change is, I am using TokenStream directly instead of using syn and quote crates. Also, if I mistype the method name, the rust compiler does suggest that a method with similar name exists.
Here is a Playground repro: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=02c1ee77bcd80c68967834a53c011e41
So, indeed what you mention is true: the expanded code could be copy-pasted and it would work. When this happens (having behavior from macro expansion and "manual copy-pasted expansion" differ), there are two possibilities:
macro_rules! metavariables
When emitting code using macro_rules! special captures, some of these captures are wrapped with special invisible parenthesis that already tell the parser how the thing inside should be parsed, which make it illegal to use in other places (for instance, one may capture a $Trait:ty, and then doing impl $Trait for ... will fail (it will parse $Trait as a type, thus leading to it being interpreted as a trait object (old syntax)); see also https://github.com/danielhenrymantilla/rust-defile for other examples.
This is not your case, but it's good to keep in mind (e.g. my initial hunch was that when doing $tt::$rhs if $tt was a :path-like capture, then that could fail).
macro hygiene/transparency and Spans
Consider, for instance:
macro_rules! let_x_42 {() => (
let x = 42;
)}
let_x_42!();
let y = x;
This expands to code that, if copy-pasted, does not fail to compile.
Basically the name x that the macro uses is "tainted" to be different from any x used outside the macro body, precisely to avoid misinteractions when the macro needs to define helper stuff such as variables.
And it turns out that this is the same thing that has happened with your from_byte identifier: your code was emitting a from_byte with private hygiene / a def_site() span, which is something that normally never happens for method names when using classic macros, or classic proc-macros (i.e., when not using the unstable ::proc_macro::quote! macro). See this comment: https://github.com/rust-lang/rust/issues/54722#issuecomment-696510769
And so the from_byte identifier is being "tainted" in a way that allows Rust to make it invisible to code not belonging to that same macro expansion, such as the code in your fn main.
The solution, at this point, is easy: forge a from_bytes Identifier with an explicit non-def_site() Span (e.g., Span::call_site(), or even better: Span::mixed_site() to mimic the rules of macro_rules! macros) so as to prevent it from getting that default def_site() Span that ::proc_macro::quote! uses:
use ::proc_macro::Span;
// ...
let from_byte = TokenTree::from(Ident::new("from_byte", Span::mixed_site()));
res.extend(quote!(impl $tt {
// use an interpolated ident rather than a "hardcoded one"
// vvvvvvvvvv
pub fn $from_byte(byte: u8) -> $tt {
match byte {
$match_arms
_ => unreachable!()
}
}
}))
Playground

What is the ownership of *self in matching expression for impl of struct in rust

I am using example from here
#[derive(Debug)]
enum List {
Cons(i32, RefCell<Rc<List>>),
Nil,
}
impl List {
fn tail(&self) -> Option<&RefCell<Rc<List>>> {
match *self {
Cons(_, ref item) => Some(item),
Nil => None,
}
}
}
Given the function signature is &self , self is of reference type pointing at List and *self is the List instance itself.
But I remember match also take ownership of the object it is matching, so doesn't that cause problem to the struct because the List instance is move to match and not give back?
Also isn't &self immutable, why are we able to move self to match?
match by itself does not move anything. Moves, copies or borrows happen in the branches of the match.
Example:
let s = "test".to_string();
let a = s; // this is a move
// println!("{}", s); // ... so this is a "use after move" compilation error
but if we do:
match a { // this is allowed, a does not move anywhere
// but in the branch...
// this is an error: it would move a to res
// res if res == "test".to_string() => "ok",
// ^^^ moves value into pattern guard
ref res if res == "test" => "ok", // with ref it's fine (we're
// taking a by reference)
_ => "ko",
}
playground
Note that you might indeed run into ownership issues in the match, but they are typically due to something you're doing after the match keyword.
For instance this fails:
// let's break the string in two pieces
match a.split_off(2)
// ^ cannot borrow mutably
but it's because of the split_off that takes &mut self, not the match on the result.

Textfile-parsing function fails to compile owing to type-mismatch error

I'm trying to parse a simple config text file, which contains one three-word entry per line, laid out as follows:
ITEM name value
ITEM name value
//etc.
I've reproduced the function which does the parsing (and the subsequent compilation error) here (and on the Rust Playpen):
pub fn parse(path: &Path) -> config_struct {
let file = File::open(&path).unwrap();
let reader = BufReader::new(&file);
let line_iterator = reader.lines();
let mut connection_map = HashMap::new();
let mut target_map = HashMap::new();
for line in line_iterator {
let line_slice = line.unwrap();
let word_vector: Vec<&str> = line_slice.split_whitespace().collect();
if word_vector.len() != 3 { continue; }
match word_vector[0] {
"CONNECTION" => connection_map.insert(word_vector[1], word_vector[2]),
"TARGET" => target_map.insert(word_vector[1], word_vector[2]),
_ => continue,
}
}
config_struct { connections: connection_map, targets: target_map }
}
pub struct config_struct<'a> {
// <name, value>
connections: HashMap<&'a str, &'a str>,
// <name, value>
targets: HashMap<&'a str, &'a str>,
}
src/parse_conf_file.rs:23:3: 27:4 error: mismatched types:
expected `()`,
found `core::option::Option<&str>`
(expected (),
found enum `core::option::Option`) [E0308]
src/parse_conf_file.rs:23 match word_vector[0] {
src/parse_conf_file.rs:24 "CONNECTION" => connection_map.insert(word_vector[1], word_vector[2]),
src/parse_conf_file.rs:25 "TARGET" => target_map.insert(word_vector[1], word_vector[2]),
src/parse_conf_file.rs:26 _ => continue,
src/parse_conf_file.rs:27 }
In essence, I seem to have created a match statement that expects an empty tuple, and also finds the contents of a Vec<&str> to be wrapped in an Option!
NB. This post originally contained two questions (that I'd believed were one error manifesting itself differently), but as-per advice in the comments I've split it into two separate posts. The latter post is here.
Your original problem is just that you have a non-() expression at the end of your loop body. Your match expression has type Option<&str> (because that is the return type of HashMap::insert), not type (). This problem is solved by simply putting a semicolon after the match expression:
match word_vector[0] {
"CONNECTION" => connection_map.insert(word_vector[1], word_vector[2]),
"TARGET" => target_map.insert(word_vector[1], word_vector[2]),
_ => continue,
};
For the latter, isn't word_vector populated with owned objects that don't point to line_slice?
No, which is precisely the issue. word_vector contains elements of type &str, i.e. borrowed strings. These point into line_slice, which only lives until the end of the current loop iteration. You probably want to convert them to Strings (using String::from) before inserting them into the map.

Resources