How to use `AsRef` arguments? - reference

I am having a hard time trying to get AsRef to work in a clean way.
const DEFAULT: &str = "lib";
use std::path::{Path, PathBuf};
fn extend(p: &Path, q: Option<&Path>) -> PathBuf {
let q: &Path = q.unwrap_or(DEFAULT.as_ref());
p.join(q)
}
// DOES NOT COMPILE
fn extend1<P: AsRef<Path>, Q: AsRef<Path>>(p: P, q: Option<Q>) -> PathBuf {
let q: &Path = q.map(|x| x.as_ref()).unwrap_or(DEFAULT.as_ref());
p.as_ref().join(q)
}
// DOES NOT COMPILE
fn extend2<P: AsRef<Path>, Q: AsRef<Path>>(p: P, q: Option<Q>) -> PathBuf {
let q: &Path = match q {
Some(x) => x.as_ref(),
None => DEFAULT.as_ref(),
};
p.as_ref().join(q)
}
fn extend3<P: AsRef<Path>, Q: AsRef<Path>>(p: P, q: Option<Q>) -> PathBuf {
match q {
Some(x) => p.as_ref().join(x.as_ref()),
None => p.as_ref().join(AsRef::<Path>::as_ref(DEFAULT)),
}
}
The function extend works with Path references, but I want to generalize it to accept arguments of type AsRef<Path> in order to allow also string, for instance.
My first attempts, extend1 and extend2, do not pass the borrow checker, which complains about the lifetime of x in both cases.
My third attempt, extend3, works, but has the obvious drawback of code duplication, which gets more severe as the function body grows.
What is the best solution in this circumstance?

Option::map consumes the inner value (if there is one). So in the code q.map(|x| x.as_ref()), the closure takes x by value. You can check this by noting that q.map(|x: &Q| x.as_ref()) gives a type error, while q.map(|x: Q| x.as_ref()) does not (it still gives the lifetime error). That means that when you call x.as_ref(), a new reference to x is created, not related to any outside reference. This means that the reference is only valid inside the closure, but you want to use it in the rest of extend1.
What you want to do instead is have a borrow of q that's valid until the end of extend1. This borrow of q can be turned into a borrow of its contents (if any) using Option::as_ref() to convert &Option<Q> into Option<&Q> (simply using q.as_ref() will create the borrow of q you need). Then when you use map, the closure will take &Q, rather than Q. The lifetime of the reference will be the same as the external borrow of q, so it will last until the end of extend1 (if needed).
const DEFAULT: &str = "lib";
use std::path::{Path, PathBuf};
fn extend1<P: AsRef<Path>, Q: AsRef<Path>>(p: P, q: Option<Q>) -> PathBuf {
let q: &Path = q.as_ref().map(|x| x.as_ref()).unwrap_or(DEFAULT.as_ref());
p.as_ref().join(q)
}
(playground)
Since the arguments to your function are only ever used by reference, you may wish to only take them by reference. This is as easy as adding an ampersand to each argument.
const DEFAULT: &str = "lib";
use std::path::{Path, PathBuf};
fn extend1<P: AsRef<Path>, Q: AsRef<Path>>(p: &P, q: &Option<Q>) -> PathBuf {
let q: &Path = q.as_ref().map(|x| x.as_ref()).unwrap_or(DEFAULT.as_ref());
p.as_ref().join(q)
}
(playground)
or
const DEFAULT: &str = "lib";
use std::path::{Path, PathBuf};
fn extend1<P: AsRef<Path>, Q: AsRef<Path>>(p: &P, q: Option<&Q>) -> PathBuf {
let q: &Path = q.map(|x| x.as_ref()).unwrap_or(DEFAULT.as_ref());
p.as_ref().join(q)
}
(playground)
Note that this last version doesn't need to call q.as_ref(), since q already has type Option<&Q>. This version most closely follows your original extend function.

Related

Taking ownership of a &String without copying

Context
Link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=9a9ffa99023735f4fbedec09e1c7ac55
Here's a contrived repro of what I'm running into
fn main() {
let mut s = String::from("Hello World");
example(&mut s);
}
fn example(s: &mut str) -> Option<String> {
other_func(Some(s.to_owned()))
// other random mutable stuff happens
}
fn other_func(s: Option<String>) {
match s {
Some(ref s) => other_func2(*s),
None => panic!()
}
}
fn other_func2(s: String) {
println!("{}", &s)
}
and the error
Compiling playground v0.0.1 (/playground)
error[E0507]: cannot move out of `*s` which is behind a shared reference
--> src/main.rs:12:36
|
12 | Some(ref s) => other_func2(*s),
| ^^ move occurs because `*s` has type `String`, which does not implement the `Copy` trait
Question
In the following code, why can't I deference the &String without having to do some sort of clone/copy? i.e. this doesn't work
fn other_func(s: Option<String>) {
match s {
Some(ref s) => other_func2(*s),
None => panic!()
}
}
but it works if I replace *s with s.to_owned()/s.to_string()/s.clone()
As an aside, I understand this can probably be solved by refactoring to use &str, but I'm specifically interested in turning &String -> String
Why would the compiler allow you to?
s is &String. And you cannot get a String from a &String without cloning. That's obvious.
And the fact that it was created from an owned String? The compiler doesn't care, and it is right. This is not different from the following code:
let s: String = ...;
let r: &String = ...;
let s2: String = *r; // Error
Which is in turn not different from the following code, for instance, as far as the compiler is concerned:
let r: &String = ...;
let s: String = *s;
And we no longer have an owned string at the beginning. In general, the compiler doesn't track data flow. And rightfully so - when it type-checks the move it doesn't even can confirm that this reference isn't aliased. Or that the owned value is not used anymore. References are just references, they give you no right to drop the value.
Changing that will not be feasible in the general case (for example, the compiler will have to track data flow across function calls), and will require some form of manual annotation to say "this value is mine". And you already have such annotation - use an owned value, String, instead of &String: this is exactly what it's about.

read file from random Vec of string

i want to read a file randomly from Vec<String> and then use it as multipart to reqwest async
but i keep getting:
here's the function
fn getFiles(dirname: &str) -> Vec<String> {
let mut items: Vec<String> = Vec::new();
let dir = std::fs::read_dir(dirname);
for item in dir.expect("fail") {
if let Ok(item) = item {
items.push(item.path().into_os_string().into_string().unwrap());
}
}
items
}
// call the function
let dir_items = getFiles("src/assets");
let file = dir_items.into_iter().choose(&mut rand::thread_rng()).unwrap();
let path = std::path::Path::new(&file);
let sub_file = std::fs::read(path)?;
// after this, file lifetime is already end ?
let sub_file_part = reqwest::multipart::Part::bytes(sub_file)
.file_name(path.file_name().unwrap().to_string_lossy())
.mime_str("application/octet-stream")?;
playground
Let's look at this piece by piece:
let path = std::path::Path::new(&file);
If you have a look at the documentation for Path::new, you see the following signature:
pub fn new<S: AsRef<OsStr> + ?Sized>(s: &S) -> &Path
After applying the second lifetime elision rules, (quote: "if there is exactly one input lifetime parameter, that lifetime is assigned to all output lifetime parameters: fn foo<'a>(x: &'a i32) -> &'a i32"), this signature really looks like this:
pub fn new<'a, S: AsRef<OsStr> + ?Sized>(s: &'a S) -> &'a Path
So, your path variable cannot live longer than your file variable.
Next, this is relevant:
.file_name(path.file_name().unwrap().to_string_lossy())
After again applying the lifetime elision rules, you can see that calling unwrap on path.file_name() gives you a &std::ffi::OsStr that can live no longer than your path variable which in turn (as discussed before) can live no longer than your file variable. So, transitively, path.filename().unwrap() can live no longer than the file variable.
Now, let's have a look at the signature of OsStr::to_string_lossy():
pub fn to_string_lossy(&self) -> Cow<'_, str>
So, this method returns a Cow<'_, str>. Cow is shorthand for "Copy or Owned" – meaning that the Cow can either contain a reference to the data (in this case, a &str, if the OsStr contained only valid UTF-8 bytes) or it can own the data itself (in this case, by containing a String, if the OsStr contained data that needed to be converted or to be filtered out during UTF-8 conversion).
'_ is a placeholder and means that the compiler shall (again) infer the lifetime with the lifetime elision rules. The expanded signature looks like this:
pub fn to_string_lossy<'a>(&'a self) -> Cow<'a, str>
So, in your example, 'a can't be any bigger than the lifetime of your &OsStr returned by unwrap(), which transitively means that 'a can't be bigger than the lifetime of file.
However, reqwest::multipart::Part::file_name() expects something implementing Into<Cow<'static, str>> as parameter. Our Cow<'a, str> definitively can't implement that, as our file variable is not alive until the end of the program. If it was alive until the end of the program, our Cow would implement it because file could be borrowed for 'static, and all would be well – this is the reason for the error message.
You can work around this by calling into_owned() on the Cow. This converts the Cow into a string, which does implement Into<Cow<'static, str>>:
use rand::prelude::*;
fn getFiles(dirname: &str) -> Vec<String> {
let mut items: Vec<String> = Vec::new();
let dir = std::fs::read_dir(dirname);
for item in dir.expect("fail") {
if let Ok(item) = item {
items.push(item.path().into_os_string().into_string().unwrap());
}
}
items
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// call the function
let dir_items = getFiles("src/assets");
let file = dir_items.into_iter().choose(&mut rand::thread_rng()).unwrap();
let path = std::path::Path::new(&file);
let sub_file = std::fs::read(path)?;
// after this, file lifetime is already end ?
let sub_file_part = reqwest::multipart::Part::bytes(sub_file)
.file_name(path.file_name().unwrap().to_string_lossy().into_owned())
.mime_str("application/octet-stream")?;
Ok(())
}
Playground

Is there a safe way to map an enum variant to another with just a mutable reference to it in Rust?

Consider the following code:
enum Deferred<F, T> {
Fn(F),
Ready(T),
}
impl<F, T> Deferred<F, T>
where
F: FnOnce() -> T,
T: Clone,
{
fn get_clone(&mut self) -> T {
let clone;
*self = match *self {
Self::Fn(f) => { // `f` moved here
let data = f(); // this needs to consume `f`
clone = data.clone();
Self::Ready(data)
},
Self::Ready(data) => { // `data` moved here
clone = data.clone();
Self::Ready(data)
},
};
clone
}
}
The compiler complains about the *self = match *self {...}; statement because its right hand side takes ownership of contents of self. Is there a way to accomplish this behaviour with just a mutable reference to self?
I found a workaround using F: FnMut() -> T so that f doesn't have to be moved but this approach clearly has its limitations.
I also tried what the answer to Is there a safe way to temporarily retrieve an owned value from a mutable reference in Rust? suggested but it led to an issue with initialization of clone (the compiler could no longer reason that the match statement would initialize clone because the code was moved into a closure) so I had to use MaybeUninit with unsafe.
At that point it was better to read/write self through a raw pointer:
unsafe {
std::ptr::write(
self as *mut Self,
match std::ptr::read(self as *const Self) {
Self::Fn(f) => {
let data = f();
clone = data.clone();
Self::Ready(data)
},
Self::Ready(data) {
clone = data.clone();
Self::Ready(data)
},
}
);
}
It is not possible to do this in a straightforward safe fashion, because while f is being executed there is no possible valid value of Deferred as defined: you don't yet have a T to go in Deferred::Ready, and you're consuming the F so you can't have Deferred::Fn.
If you use the take_mut crate or similar, that accomplishes this by replacing panicking with aborting, so the invalid state can never be observed. But, in your case, I would recommend introducing a third state to the enum instead — this changes the semantics but in a way that, shall we say, respects the fact that f can fail.
enum Deferred<F, T> {
Fn(F),
Ready(T),
Failed,
}
impl<F, T> Deferred<F, T>
where
F: FnOnce() -> T,
T: Clone,
{
fn get_clone(&mut self) -> T {
match std::mem::replace(self, Self::Failed) {
Self::Ready(data) => {
let clone = data.clone();
*self = Self::Ready(data);
clone
},
Self::Fn(f) => {
let data = f();
*self = Self::Ready(data.clone());
data
}
Self::Failed => {
panic!("A previous call failed");
}
}
}
}
With this solution, the very first thing we do is swap out *self for Self::Failed, so we own the Deferred value and can freely move out the non-clonable F value. Notice that the expression being matched is not a borrow of self, so we aren't blocked from further modifying *self.
This particular solution does have a disadvantage: it's unnecessarily writing to *self on every call (which is unobservable, but could reduce performance). We can fix that, by separating the decision of what to do from doing it, but this requires writing a second pattern match to extract the value:
impl<F, T> Deferred<F, T>
where
F: FnOnce() -> T,
T: Clone,
{
fn get_clone(&mut self) -> T {
match self {
Self::Ready(data) => {
return data.clone();
},
Self::Failed => {
panic!("A previous call failed");
}
Self::Fn(_) => {
// Fall through below, relinquishing the borrow of self.
}
}
match std::mem::replace(self, Self::Failed) {
Self::Fn(f) => {
let data = f();
*self = Self::Ready(data.clone());
data
}
_ => unreachable!()
}
}
}
The fundamental problem here is trying to take ownership of the value in the enum while simultaneously having a live reference (via &mut self) to it. Safe Rust code will not let you do that, which is the reason for the compiler error. This is also a problem with your unsafe-solution: What if any code inside one of the match-arms panics? Then the owned value created via ptr::read gets dropped, the stack unwinds, and the caller might catch that panic and is then perfectly capable of observing the Deferred it owns (of which it gave a &mut to get_clone()) after it has been dropped, that is, in an invalid state and therefor causing Undefined Behaviour (see the docs for ptr::read for an example).
What must be achieved therefore is to hold &mut self in a valid/defined state while taking ownership. You can do that by having a cheap or possibly even free variant on Deferred, which is put into &mut self while the new value is being constructed.
For instance:
#[derive(Debug)]
enum FooBar {
Foo(String),
Bar(&'static str),
}
impl FooBar {
fn switch(&mut self) {
// notice here
*self = match std::mem::replace(self, FooBar::Bar("temporary")) {
FooBar::Foo(_) => FooBar::Bar("default"),
FooBar::Bar(s) => FooBar::Foo(s.to_owned()),
}
}
}
fn main() {
let mut s = FooBar::Bar("hello");
s.switch();
println!("{:?}", s);
}
Here, we use std::mem::replace to switch the value behind &mut self with a cheaply constructed temporary value; replace() returns ownership of the original value, which we match on to construct a new value; the value returned by the match-expression is then put into place via the *self-assignment. If everything goes well, the FooBar::Bar("temporary") is not ever observed; but it could be observed if the match panicked; but even then, the code is at least safe. In case your match can't unwind at all, the compiler might even be able to eliminate that useless store entirely.
Coming back to your original code, I don't see how to construct a Deferred without T being Default, as you can neither construct the Fn nor the Ready case in a safe way. Either that can be added via an additional bound, or you can add a third variant Empty to your enum.
As I see it, the difference between your problem and the one that you referenced is that you also want to return the inner T while replacing self. Which is something that the there proposed crate take_mut does not provide. However, I noticed that it seems a bit unmaintained. So, I had a short look around, and found the replace_with crate instead. It basically does the same as take_mut, but it also has a replace_with_or_abort_and_return function -- exactly what you need:
use replace_with::replace_with_or_abort_and_return;
impl<F, T> Deferred<F, T>
where
F: FnOnce() -> T,
T: Clone,
{
fn get_clone(&mut self) -> T {
replace_with_or_abort_and_return(self, |s| match s {
Self::Fn(f) => {
// `f` moved here
let data = f(); // this needs to consume `f`
let clone = data.clone();
(clone, Self::Ready(data))
}
Self::Ready(data) => {
// `data` moved here
let clone = data.clone();
(clone, Self::Ready(data))
}
})
}
}
Tho, notice, that taking a value out of a mutable borrow, has the potential of UB that is specifically if your f() panics, because self would be left in an uninitialized state. Therefore, we get this slightly cumbersome function name, which indicates that if f() panic, the program will be aborted (which is the safe thing to do). However, if you expect f() to panic and don't want to abort the program, you can instead provide a default for self via the replace_with_and_return or replace_with_or_default_and_return function.

Borrowed value not living long enough (BufReader lines() to iterator of String)

With this sample code:
use std::fs::{File};
use std::io::{BufRead, BufReader};
use std::path::Path;
type BoxIter<T> = Box<Iterator<Item=T>>;
fn tokens_from_str<'a>(text: &'a str)
-> Box<Iterator<Item=String> + 'a> {
Box::new(text.lines().flat_map(|s|
s.split_whitespace().map(|s| s.to_string())
))
}
// Returns an iterator of an iterator. The use case is a very large file where
// each line is very long. The outer iterator goes over the file's lines.
// The inner iterator returns the words of each line.
pub fn tokens_from_path<P>(path_arg: P)
-> BoxIter<BoxIter<String>>
where P: AsRef<Path> {
let reader = reader_from_path(path_arg);
let iter = reader.lines()
.filter_map(|result| result.ok())
.map(|s| tokens_from_str(&s));
Box::new(iter)
}
fn reader_from_path<P>(path_arg: P) -> BufReader<File>
where P: AsRef<Path> {
let path = path_arg.as_ref();
let file = File::open(path).unwrap();
BufReader::new(file)
}
I get this compiler error message:
rustc 1.18.0 (03fc9d622 2017-06-06)
error: `s` does not live long enough
--> <anon>:23:35
|
23 | .map(|s| tokens_from_str(&s));
| ^- borrowed value only lives until here
| |
| does not live long enough
|
= note: borrowed value must be valid for the static lifetime...
My questions are:
How can this be fixed (without changing the function signatures, if possible?)
Any suggestions on better function arguments and return values?
One issue is that .split_whitespace() takes a reference, and doesn't own its content. So when you try to construct a SplitWhitespace object with an owned owned object (this happens when you call .map(|s| tokens_from_str(&s))), the string s is dropped while SplitWhitespace is still trying to reference it. I wrote a quick fix to this by creating a struct that takes ownership of the String and yields a SplitWhitespace on demand.
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;
use std::iter::IntoIterator;
use std::str::SplitWhitespace;
pub struct SplitWhitespaceOwned(String);
impl<'a> IntoIterator for &'a SplitWhitespaceOwned {
type Item = &'a str;
type IntoIter = SplitWhitespace<'a>;
fn into_iter(self) -> Self::IntoIter {
self.0.split_whitespace()
}
}
// Returns an iterator of an iterator. The use case is a very large file where
// each line is very long. The outer iterator goes over the file's lines.
// The inner iterator returns the words of each line.
pub fn tokens_from_path<P>(path_arg: P) -> Box<Iterator<Item = SplitWhitespaceOwned>>
where P: AsRef<Path>
{
let reader = reader_from_path(path_arg);
let iter = reader
.lines()
.filter_map(|result| result.ok())
.map(|s| SplitWhitespaceOwned(s));
Box::new(iter)
}
fn reader_from_path<P>(path_arg: P) -> BufReader<File>
where P: AsRef<Path>
{
let path = path_arg.as_ref();
let file = File::open(path).unwrap();
BufReader::new(file)
}
fn main() {
let t = tokens_from_path("test.txt");
for line in t {
for word in &line {
println!("{}", word);
}
}
}
Disclaimer: Frame Challenge
When dealing with huge files, the simplest solution is to use Memory Mapped Files.
That is, you tell the OS that you want the whole file to be accessible in memory, and it's up to it to deal with paging parts of the file in and out of memory.
Once this is down, then your whole file is accessible as a &[u8] or &str (at your convenience), and you can trivially access slices of it.
It may not always be the fastest solution; it certainly is the easiest.
The problem here is that you do use to_string() to turn each item into an owned value, this is done lazily. Since it's lazy, the value before to_string is used (a &str) still exists in the returned iterator's state, and thus is invalid (since the source String is dropped as soon as your map closure returns).
Naive solution
The simplest solution here is to remove the lazy evaluation for that part of the iterator, and simply allocate all tokens as soon as the line is allocated. This will not be as fast, and will involve an additional allocation, but has minimal changes from your current function, and keeps the same signature:
// Returns an iterator of an iterator. The use case is a very large file where
// each line is very long. The outer iterator goes over the file's lines.
// The inner iterator returns the words of each line.
pub fn tokens_from_path<P>(path_arg: P) -> BoxIter<BoxIter<String>>
where
P: AsRef<Path>
{
let reader = reader_from_path(path_arg);
let iter = reader.lines()
.filter_map(|result| result.ok())
.map(|s| {
let collected = tokens_from_str(&s).collect::<Vec<_>>();
Box::new(collected.into_iter()) as Box<Iterator<Item=String>>
});
Box::new(iter)
}
This solution will be fine for any small workload, and it will only allocate about twice as much memory for the line at the same time. There's a performance penalty, but unless you have 10mb+ lines, it probably won't matter.
If you do choose this solution, I'd recommend changing the function signature of tokens_from_path to directly return a BoxIter<String>:
pub fn tokens_from_path<P>(path_arg: P) -> BoxIter<String>
where
P: AsRef<Path>
{
let reader = reader_from_path(path_arg);
let iter = reader.lines()
.filter_map(|result| result.ok())
.flat_map(|s| {
let collected = tokens_from_str(&s).collect::<Vec<_>>();
Box::new(collected.into_iter()) as Box<Iterator<Item=String>>
});
Box::new(iter)
}
Alternative: decouple tokens_from_path and tokens_from_str
The original code doesn't work because you are trying to return borrows to a String which you don't return.
We can fix that by returning the String instead - just hidden behind an opaque API. This is pretty similar to breeden's solution, but slightly different in execution.
use std::fs::{File};
use std::io::{BufRead, BufReader};
use std::path::Path;
type BoxIter<T> = Box<Iterator<Item=T>>;
/// Structure representing in our code a line, but with an opaque API surface.
pub struct TokenIntermediate(String);
impl<'a> IntoIterator for &'a TokenIntermediate {
type Item = String;
type IntoIter = Box<Iterator<Item=String> + 'a>;
fn into_iter(self) -> Self::IntoIter {
// delegate to tokens_from_str
tokens_from_str(&self.0)
}
}
fn tokens_from_str<'a>(text: &'a str) -> Box<Iterator<Item=String> + 'a> {
Box::new(text.lines().flat_map(|s|
s.split_whitespace().map(|s| s.to_string())
))
}
// Returns an iterator of an iterator. The use case is a very large file where
// each line is very long. The outer iterator goes over the file's lines.
// The inner iterator returns the words of each line.
pub fn token_parts_from_path<P>(path_arg: P) -> BoxIter<TokenIntermediate>
where
P: AsRef<Path>
{
let reader = reader_from_path(path_arg);
let iter = reader.lines()
.filter_map(|result| result.ok())
.map(|s| TokenIntermediate(s));
Box::new(iter)
}
fn reader_from_path<P>(path_arg: P) -> BufReader<File>
where P: AsRef<Path> {
let path = path_arg.as_ref();
let file = File::open(path).unwrap();
BufReader::new(file)
}
As you notice, there's no difference to tokens_from_str, and tokens_from_path just returns this opaque TokenIntermediate struct. This will be just as usable as your original solution, all it does is push the ownership of your intermediate String values onto the caller, so they can iterate the tokens in them.

How to convert an Iterator on a tuple of (String, String) to an Iterator of (&str, &str)?

I'm having trouble converting from an Iterator of (String, String) to an Iterator of (&str, &str). I'm using an external library, so can't change the signature of that, and not sure that I need to. Basically I have this function def:
use hyper;
fn build_url<'a, I>(host: &'a str, port: u16, path: &'a str, params: I) ->
hyper::Url where I: Iterator<Item=(String, String)> {
let mut url = hyper::Url::parse(&format!("http://{h}:{p}/{pt}",
h = self.etcd_host,
p = self.etcd_port,
pt = path));
if let Err(e) = url {
panic!("error parsing url: {}", e);
}
let mut url = url.unwrap();
// fn set_query_from_pairs<'a, I>(&mut self, pairs: I)
// where I: Iterator<Item=(&'a str, &'a str)>
url.set_query_from_pairs(
params.map(|x: (String, String)| ->
(&str, &str) { let (ref k, ref v) = x; (k, v) } ));
}
But I'm getting the dreaded: error: 'x.0' does not live long enough
I think the ref keyword in the let should have been the right thing here, i.e. keep the ownership with the Iterator, and just do a borrow. I get a similar issue if I get rid of ref in the let changing the let to this:
let (k, v) = x; (&k, &v)
Then k and v don't live long enough. Does anyone have a recommendation for fixing this?
You can't have an iterator that (safely) yields references to any internal or owned state; the Iterator trait is just not designed to allow it. These sorts of constructs are usually known as "streaming iterators", and they're something of a hole in the language/stdlib at the moment.
Consider what happens to a (String, String) value as it flows through your map call. Each tuple is returned from I::next, which causes ownership to pass into the closure you gave to map. Thus, when you use ref in the closure, you're taking a reference to variables which are local to the closure. You now construct a new tuple, return it and... because the closure owns the Strings (they're being stored in k and v), they are destroyed, thus invalidating the references you tried to return.
The problem is that there is no way to avoid taking ownership of the (String, String) items.
Now, that having been said, you can cheat here. All you need to do is guarantee that the (String, String) values continue to exist beyond each individual step in the iterator. Thus:
let params: Vec<_> = params.collect();
url.set_query_from_pairs(params.iter().map(|&(ref x, ref y)| (&x[..], &y[..])))
This works because Vec::iter gives us Iterator<Item=&(String, String)>, from which we can borrow without taking ownership (which is retained by params).
Since your params argument is created from a Vec<(String, String)> you can change your where clause to where I: Iterator<Item=(&str, &str)> and get the iterator by calling
your_vector.iter().map(|(a, b)|, (&a[..], &b[..])))
A simplified example:
fn test<'a, I>(it: I)
where I: Iterator<Item=&'a str>
{
for s in it {
dump(s);
}
}
fn dump(s: &str) {
println!("{}", s);
}
fn main() {
let v = vec!["a".to_string(), "42".to_string()];
test(v.iter().map(|s| &s[..]));
}

Resources