How do I send an interpolated string on a channel? - rust

How do I store a variable in a string? I've read the examples, but they are all just println!().
//javascript
var url_str = "http://api.weather/city" + city_code + "/get";
//go
urlStr := fmt.Sprintf("http://api.weather/%s/get", cityCode)
// Edit: Rust
let url_str = format!("http://api.openweathermap.org/data/2.5/weather?q={}", city_code);
I am using tx.send() and want to send an interpolated string on the channel like this:
let url_str = "http://api.weather";
c.send(url_str);
but I get an error
src/http_get/http_getter.rs:21:17: 21:24 error: `url_str` does not live long enough
src/http_get/http_getter.rs:21 c.send(&url_str);
^~~~~~~
Here is the function that I am trying to implement for constructing the URL:
pub fn construct_url(c: &Sender<String>, city_code: &str) {
let url_str = format!("http://api.openweathermap.org/data/2.5/weather?q={}", city_code);
println!("{}", url_str);
c.send(url_str);
}

With elided lifetimes and types reinstated, here’s what you have:
pub fn construct_url<'a, 'b, 'c>(c: &'a Sender<&'b str>, city_code: &'c str) {
let url_str: String = format!("http://api.openweathermap.org/data/2.5/weather?q={}", city_code);
println!("{}", url_str);
c.send(&url_str);
}
Bear in mind the distinctions between String and &str: &str is a string slice, a reference to a string that someone else owns; String is the owned variety.
'b is necessarily at least as long as the entire function body—any string you construct inside the function will not live long enough for 'b. Therefore your sender will need to send a String, not a &str.
pub fn construct_url(c: &Sender<String>, city_code: &str) {
let url_str = format!("http://api.openweathermap.org/data/2.5/weather?q={}", city_code);
println!("{}", url_str);
c.send(url_str);
}

Related

Temporarily cache owned value between iterator adapters

I'd like to know if there's a way to cache an owned value between iterator adapters, so that adapters later in the chain can reference it.
(Or if there's another way to allow later adapters to reference an owned value that lives inside the iterator chain.)
To illustrate what I mean, let's look at this (contrived) example:
I have a function that returns a String, which is called in an Iterator map() adapter, yielding an iterator over Strings. I'd like to get an iterator over the chars() in those Strings, but the chars() method requires a string slice, meaning a reference.
Is this possible to do, without first collecting the Strings?
Here's a minimal example that of course fails:
fn greet(c: &str) -> String {
"Hello, ".to_owned() + c
}
fn main() {
let names = ["Martin", "Helena", "Ingrid", "Joseph"];
let iterator = names.into_iter().map(greet);
let fails = iterator.flat_map(<str>::chars);
}
Playground
Using a closure instead of <str>::chars - |s| s.chars() - does of course not work either. It makes the types match, but breaks lifetimes.
Edit (2022-10-03): In response to the comments, here's some pseudocode of what I have in mind, but with incorrect lifetimes:
struct IteratorCache<'a, T, I>{
item : Option<T>,
inner : I,
_p : core::marker::PhantomData<&'a T>
}
impl<'a, T, I> Iterator for IteratorCache<'a, T,I>
where I: Iterator<Item=T>
{
type Item=&'a T;
fn next(&mut self) -> Option<&'a T> {
self.item = self.inner.next();
if let Some(x) = &self.item {
Some(&x)
} else {
None
}
}
}
The idea would be that the reference could stay valid until the next call to next(). However I don't know if this can be expressed with the function signature of the Iterator trait. (Or if this can be expressed at all.)
I don't think something like this exists yet, and collecting into a Vec<char> creates some overhead, but you can write such an iterator yourself with a little bit of trickery:
struct OwnedCharsIter {
s: String,
index: usize,
}
impl OwnedCharsIter {
pub fn new(s: String) -> Self {
Self { s, index: 0 }
}
}
impl Iterator for OwnedCharsIter {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
// Slice of leftover characters
let slice = &self.s[self.index..];
// Iterator over leftover characters
let mut chars = slice.chars();
// Query the next char
let next_char = chars.next()?;
// Compute the new index by looking at how many bytes are left
// after querying the next char
self.index = self.s.len() - chars.as_str().len();
// Return next char
Some(next_char)
}
}
fn greet(c: &str) -> String {
"Hello, ".to_owned() + c
}
fn main() {
let names = ["Martin", "Helena", "Ingrid", "Joseph"];
let iterator = names.into_iter().map(greet);
let chars_iter = iterator.flat_map(OwnedCharsIter::new);
println!("{:?}", chars_iter.collect::<String>())
}
"Hello, MartinHello, HelenaHello, IngridHello, Joseph"

Merging two adjacent string slices [duplicate]

This question already has answers here:
Rust: Is there an opposite for split_at_mut (i.e. join_mut)?
(2 answers)
Closed 8 months ago.
When writing a parser I ran into the problem that there are two string slices that come from the same origin string and are next to each other in memory. Of course it would be possible to simply copy the strings and merge them back into one, but that would require unnecessary computational resources. Is there a clean way to solve this in rust without unsafe code? For better illustration, here is an example of how I would like to solve it:
fn main() {
//This is the owned string.
//(Of course, this is also just a slice of a static string, but that makes no difference here).
let origin: &str = "Hello World";
//Substrings which borrow data from the original and should be adjacent in memory
let a: &str = &origin[0..5];
let b: &str = &origin[5..11];
//If the representation of a and b on the stack is:
// a: { ptr: PointerA, len: LenA }
// b: { ptr: PointerB, len: LenB }
//Then PointerA + LenA should be PointerB
//From this I conclude that there must be a way to combine these strings into c,
//which in turn would have this representation on the stack:
// c: { ptr: PointerA, len: LenA + LenB }
//The merge method doesn't actually exist, it's just a example of how I would imagen the api to look like.
let c = a.merge(b).unwrap();
assert!(c == origin)
}
Contrast this with the more inefficient current solution:
fn main() {
let origin: &str = "Hello World";
let a: &str = &origin[0..5];
let b: &str = &origin[5..11];
//Here both strings are simply copied to another location in the heap
//and need unnecessarily more memory, because the stored data exactly matches the data in origin
let c = a.to_owned() + b;
assert!(c == origin)
}
EDIT: This is a example of how i would implemented this with unsafe code, but i really don't know if it is actually safe
fn main() {
let origin: &str = "Hello World";
let a: &str = &origin[0..5];
let b: &str = &origin[5..11];
let c = merge(a, b).unwrap();
assert!(c == origin)
}
fn merge<'a>(one: &'a str, two: &'a str) -> Option<&'a str> {
unsafe {
let one: [usize; 2] = std::intrinsics::transmute(one);
let two: [usize; 2] = std::intrinsics::transmute(two);
if let Some(len) = one[1].checked_add(two[1]) {
if one[0] + one[1] == two[0] {
Some(std::intrinsics::transmute([one[0], len]))
} else {
None
}
} else {
None
}
}
}
You can chain the character within the splitted string:
fn main() {
//This is the owned string.
//(Of course, this is also just a slice of a static string, but that makes no difference here).
let origin: &str = "Hello World";
//Substrings which borrow data from the original and should be adjacent in memory
let a: &str = &origin[0..5];
let b: &str = &origin[5..11];
let c = a.chars().chain(b.chars());
assert_eq!(c.collect::<String>().as_str(), origin);
}
Playground
But notice that for most operation requiring &str, you would have to create a new string anyway.
So it bring us to unsafe life, and as pointed by #chayimfriedman it is UB:
use std::{slice, str};
fn main() {
//This is the owned string.
//(Of course, this is also just a slice of a static string, but that makes no difference here).
let origin: &str = "Hello World";
//Substrings which borrow data from the original and should be adjacent in memory
let a: &str = &origin[0..5];
let b: &str = &origin[5..11];
let c: &str = merge(a, b).unwrap();
assert_eq!(c, origin);
}
fn merge<'a>(a: &'a str, b: &'a str) -> Result<&'a str, String> {
let a_len = a.len();
let a_ptr = a.as_ptr();
let b_ptr = b.as_ptr();
let b_len = b.len();
if a_ptr as usize + a_len != b_ptr as usize {
return Err("Strings are not alighned".to_string());
}
Ok(unsafe {
str::from_utf8_unchecked(slice::from_raw_parts(a_ptr as *const u8, a_len + b_len))
})
}
Playground
Note that eventually you could avoid the pointers casting and instead use ptr::addr, which as for rust 1.62 it is still on nightly.

Pass mut reference to a function, and get it back

I try to write a code sample since an hour, who add some spaces in a String. So the main borrow a String to a function who add spaces, and then ,I wanna get the string back in the main function.
fn main() {
...
let mut line = String::new();
line = make_spaces(5, &mut line);
}
fn make_spaces(number: u8, string: &mut String) -> &mut String {
for _ in 0..number {
string.push(' ');
}
string
}
But the borrow checker give me the following error :
error[E0308]: mismatched types
--> src/main.rs:14:12
|
14 | line = make_spaces(left_spaces, &mut line);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| expected struct `String`, found `&mut String`
| help: try using a conversion method: `make_spaces(left_spaces, &mut line).to_string()`
I'm new with rust, and I know I don't understand borrowing. But I searched the net, and I don't understand it any better.
As I understand, I give the line ownership to make_spaces function, and then I (try to) give the ownership of string back to the main function.
Could you tell me where I'm wrong what is the solution of this problem please ? I know it's a common issue, but I don't see anything simple about that on stackoverflow.
There are two ways to do what you're trying, but you're mixing the two:
Take a mutable parameter as a reference
fn main() {
let mut line = String::new();
make_spaces(5, &mut line);
}
fn make_spaces(number: u8, string: &mut String) {
for _ in 0..number {
string.push(' ');
}
}
In that case, the caller gives you a reference to a struct so that you can modify it in place. There is no need to return anything.
Move the parameter into the function, then return the modified value
fn main() {
let mut line = String::new();
line = make_spaces(5, line);
}
fn make_spaces(number: u8, mut string: String) -> String {
for _ in 0..number {
string.push(' ');
}
string
}
In that case, the string is moved into the make_spaces function, so you need to move it back to the caller by returning it.
You don't have to return anything: you received a reference to a struct the caller of the function already has.
Just do
fn main() {
let mut line = String::new();
make_spaces(5, &mut line);
// use line here
}
fn make_spaces(number: u8, string: &mut String) {
for _ in 0..number {
string.push(' ');
}
}

error: `line` does not live long enough but it's ok in playground

I can't figure it out why my local var line does not live long enough. You can see bellow my code. It work on the Rust's playground.
I may have an idea of the issue: I use a structure (load is a function of this structure). As I want to store the result of the line in a member of my struct, it could be the issue. But I don't see what should I do to resolve this problem.
pub struct Config<'a> {
file: &'a str,
params: HashMap<&'a str, &'a str>
}
impl<'a> Config<'a> {
pub fn new(file: &str) -> Config {
Config { file: file, params: HashMap::new() }
}
pub fn load(&mut self) -> () {
let f = match fs::File::open(self.file) {
Ok(e) => e,
Err(e) => {
println!("Failed to load {}, {}", self.file, e);
return;
}
};
let mut reader = io::BufReader::new(f);
let mut buffer = String::new();
loop {
let result = reader.read_line(&mut buffer);
if result.is_ok() && result.ok().unwrap() > 0 {
let line: Vec<String> = buffer.split("=").map(String::from).collect();
let key = line[0].trim();
let value = line[1].trim();
self.params.insert(key, value);
}
buffer.clear();
}
}
...
}
And I get this error:
src/conf.rs:33:27: 33:31 error: `line` does not live long enough
src/conf.rs:33 let key = line[0].trim();
^~~~
src/conf.rs:16:34: 41:6 note: reference must be valid for the lifetime 'a as defined on the block at 16:33...
src/conf.rs:16 pub fn load(&mut self) -> () {
src/conf.rs:17 let f = match fs::File::open(self.file) {
src/conf.rs:18 Ok(e) => e,
src/conf.rs:19 Err(e) => {
src/conf.rs:20 println!("Failed to load {}, {}", self.file, e);
src/conf.rs:21 return;
...
src/conf.rs:31:87: 37:14 note: ...but borrowed value is only valid for the block suffix following statement 0 at 31:86
src/conf.rs:31 let line: Vec<String> = buffer.split("=").map(String::from).collect();
src/conf.rs:32
src/conf.rs:33 let key = line[0].trim();
src/conf.rs:34 let value = line[1].trim();
src/conf.rs:35
src/conf.rs:36 self.params.insert(key, value);
...
There are three steps in realizing why this does not work.
let line: Vec<String> = buffer.split("=").map(String::from).collect();
let key = line[0].trim();
let value = line[1].trim();
self.params.insert(key, value);
line is a Vec of Strings, meaning the vector owns the strings its containing. An effect of this is that when the vector is freed from memory, the elements, the strings, are also freed.
If we look at string::trim here, we see that it takes and returns a &str. In other words, the function does not allocate anything, or transfer ownership - the string it returns is simply a slice of the original string. So if we were to free the original string, the trimmed string would not have valid data.
The signature of HashMap::insert is fn insert(&mut self, k: K, v: V) -> Option<V>. The function moves both the key and the value, because these needs to be valid for as long as they may be in the hashmap. We would like to give the hashmap the two strings. However, both key and value are just references to strings which is owned by the vector - we are just borrowing them - so we can't give them away.
The solution is simple: copy the strings after they have been split.
let line: Vec<String> = buffer.split("=").map(String::from).collect();
let key = line[0].trim().to_string();
let value = line[1].trim().to_string();
self.params.insert(key, value);
This will allocate two new strings, and copy the trimmed slices into the new strings.
We could have moved the string out of the vector(ie. with Vec::remove), if we didn't trim the strings afterwards; I was unable to find a easy way of trimming a string without allocating a new one.
In addition, as malbarbo mentions, we can avoid the extra allocation that is done with map(String::from), and the creation of the vector with collect(), by simply omitting them.
In this case you have to use String instead of &str. See this to understand the difference.
You can also eliminate the creation of the intermediate vector and use the iterator return by split direct
pub struct Config<'a> {
file: &'a str,
params: HashMap<String, String>
}
...
let mut line = buffer.split("=");
let key = line.next().unwrap().trim().to_string();
let value = line.next().unwrap().trim().to_string();

Converting from Option<String> to Option<&str>

Very often I have obtained an Option<String> from a calculation, and I would like to either use this value or a default hardcoded value.
This would be trivial with an integer:
let opt: Option<i32> = Some(3);
let value = opt.unwrap_or(0); // 0 being the default
But with a String and a &str, the compiler complains about mismatched types:
let opt: Option<String> = Some("some value".to_owned());
let value = opt.unwrap_or("default string");
The exact error here is:
error[E0308]: mismatched types
--> src/main.rs:4:31
|
4 | let value = opt.unwrap_or("default string");
| ^^^^^^^^^^^^^^^^
| |
| expected struct `std::string::String`, found reference
| help: try using a conversion method: `"default string".to_string()`
|
= note: expected type `std::string::String`
found type `&'static str`
One option is to convert the string slice into an owned String, as suggested by rustc:
let value = opt.unwrap_or("default string".to_string());
But this causes an allocation, which is undesirable when I want to immediately convert the result back to a string slice, as in this call to Regex::new():
let rx: Regex = Regex::new(&opt.unwrap_or("default string".to_string()));
I would rather convert the Option<String> to an Option<&str> to avoid this allocation.
What is the idiomatic way to write this?
As of Rust 1.40, the standard library has Option::as_deref to do this:
fn main() {
let opt: Option<String> = Some("some value".to_owned());
let value = opt.as_deref().unwrap_or("default string");
}
See also:
How can I iterate on an Option<Vec<_>>?
You can use as_ref() and map() to transform an Option<String> into an Option<&str>.
fn main() {
let opt: Option<String> = Some("some value".to_owned());
let value = opt.as_ref().map(|x| &**x).unwrap_or("default string");
}
First, as_ref() implicitly takes a reference on opt, giving an &Option<String> (because as_ref() takes &self, i.e. it receives a reference), and turns it into an Option<&String>. Then we use map to convert it to an Option<&str>. Here's what &**x does: the rightmost * (which is evaluated first) simply dereferences the &String, giving a String lvalue. Then, the leftmost * actually invokes the Deref trait, because String implements Deref<Target=str>, giving us a str lvalue. Finally, the & takes the address of the str lvalue, giving us a &str.
You can simplify this a bit further by using map_or to combine map and unwrap_or in a single operation:
fn main() {
let opt: Option<String> = Some("some value".to_owned());
let value = opt.as_ref().map_or("default string", |x| &**x);
}
If &**x looks too magical to you, you can write String::as_str instead:
fn main() {
let opt: Option<String> = Some("some value".to_owned());
let value = opt.as_ref().map_or("default string", String::as_str);
}
or String::as_ref (from the AsRef trait, which is in the prelude):
fn main() {
let opt: Option<String> = Some("some value".to_owned());
let value = opt.as_ref().map_or("default string", String::as_ref);
}
or String::deref (though you need to import the Deref trait too):
use std::ops::Deref;
fn main() {
let opt: Option<String> = Some("some value".to_owned());
let value = opt.as_ref().map_or("default string", String::deref);
}
For either of these to work, you need to keep an owner for the Option<String> as long as the Option<&str> or unwrapped &str needs to remain available. If that's too complicated, you could use Cow.
use std::borrow::Cow::{Borrowed, Owned};
fn main() {
let opt: Option<String> = Some("some value".to_owned());
let value = opt.map_or(Borrowed("default string"), |x| Owned(x));
}
A nicer way could be to implement this generically for T: Deref:
use std::ops::Deref;
trait OptionDeref<T: Deref> {
fn as_deref(&self) -> Option<&T::Target>;
}
impl<T: Deref> OptionDeref<T> for Option<T> {
fn as_deref(&self) -> Option<&T::Target> {
self.as_ref().map(Deref::deref)
}
}
which effectively generalizes as_ref.
Although I love Veedrac's answer (I used it), if you need it at just one point and you would like something that is expressive you can use as_ref(), map and String::as_str chain:
let opt: Option<String> = Some("some value".to_string());
assert_eq!(Some("some value"), opt.as_ref().map(String::as_str));
Here's one way you can do it. Keep in mind that you have to keep the original String around, otherwise what would the &str be a slice into?
let opt = Some(String::from("test")); // kept around
let unwrapped: &str = match opt.as_ref() {
Some(s) => s, // deref coercion
None => "default",
};
playpen

Resources