I am trying to concatenate every element in a slice of &strs (&[&str]) into a single owned String. For example, I want to turn &['Hello', ' world!'] into "Hello world!".
I tried to do this by converting the slice into an iterator, then mapping over the iterator and converting each &str into an owned String, then collect them all into a Vec<String> and running .join(""), but I keep getting a type error.
Here is my code:
fn concat_str(a: &[&str]) -> String {
a.into_iter().map(|s| s.to_owned()).collect::<Vec<String>>().join("")
}
fn main() {
let arr = ["Dog", "Cat", "Bird", "Lion"];
println!("{}", concat_str(&arr[..3])); // should print "DogCatBird"
println!("{}", concat_str(&arr[2..])); // should print "BirdLion"
println!("{}", concat_str(&arr[1..3])); // should print "CatBird"
}
And here is the compiler error that I am getting:
error[E0277]: a value of type `Vec<String>` cannot be built from an iterator over elements of type `&str`
--> code.rs:2:38
|
2 | a.into_iter().map(|s| s.to_owned()).collect::<Vec<String>>().join("")
| ^^^^^^^ value of type `Vec<String>` cannot be built from `std::iter::Iterator<Item=&str>`
|
= help: the trait `FromIterator<&str>` is not implemented for `Vec<String>`
note: required by a bound in `collect`
--> /Users/michaelfm1211/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/iter/traits/iterator.rs:1780:19
|
1780 | fn collect<B: FromIterator<Self::Item>>(self) -> B
| ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `collect`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.
It says that I cannot collect into a Vec<String> because the iterator is not of a String, but I thought that I had converted it to a String with .map(|s| s.to_owned()).
How can I fix this? I am also new to rust, so it would be very helpful is someone could explain what I did wrong.
into_iter will yield an iterator with Item=&&str. In your map, .to_owned() converts that into a &str, which doesn't work. There are a few ways to fix that, you could use .copied or .cloned to get a &str:
a.into_iter().copied().map(|s| s.to_owned()).collect::<Vec<String>>().join("")
// or
a.into_iter().cloned().map(|s| s.to_owned()).collect::<Vec<String>>().join("")
Or you could use .to_string() to get a String directly:
a.into_iter().map(|s| s.to_string()).collect::<Vec<String>>().join("")
Note, you can also just collect into a String when you don't want a separator directly:
a.into_iter().map(|s| s.to_string()).collect::<String>()
There are the methods join and concat on slice directly, so you can write:
fn concat_str(a: &[&str]) -> String {
a.concat()
}
which gives you the desired output
fn concat_str(a: &[&str]) -> String {
a.iter()
.cloned()
.collect()
}
Related
I'd like to split a string by a char, then for each of those split chunks, append a new string. Then I'd like to rejoin these chunks into a single string.
fn main(){
let my_string_start = String::From("Hello, Goodmorning");
let augmented_string = my_string_start split(",").flat_map(|f| format!("{} and",f)).collect();
assert_eq!(augmented_string, String::from("Hello, and Goodmorning"));
}
But this gives error:
error[E0277]: `String` is not an iterator
--> src/main.rs:3:53
|
3 | let augmented_string = &my_string_start.split(",").flat_map(|f| format!("{} and",f)).collect();
| ^^^^^^^^ `String` is not an iterator; try calling `.chars()` or `.bytes()`
|
= help: the trait `Iterator` is not implemented for `String`
= note: required because of the requirements on the impl of `IntoIterator` for `String`
error[E0599]: the method `collect` exists for struct `FlatMap<std::str::Split<'_, &str>, String, [closure#src/main.rs:3:62: 3:85]>`, but its trait bounds were not satisfied
--> src/main.rs:3:87
|
3 | let augmented_string = &my_string_start.split(",").flat_map(|f| format!("{} and",f)).collect();
| ^^^^^^^ method cannot be called on `FlatMap<std::str::Split<'_, &str>, String, [closure#src/main.rs:3:62: 3:85]>` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`String: IntoIterator`
which is required by `FlatMap<std::str::Split<'_, &str>, String, [closure#src/main.rs:3:62: 3:85]>: Iterator`
`FlatMap<std::str::Split<'_, &str>, String, [closure#src/main.rs:3:62: 3:85]>: Iterator`
which is required by `&mut FlatMap<std::str::Split<'_, &str>, String, [closure#src/main.rs:3:62: 3:85]>: Iterator`
What's a valid way to do this?
Playground
Two points:
You forgot what flat_map actually does.
It runs over an iterator and for each element it's expected to output a separate Iterator, those iterators are chained together into a single Iterator.
In your case you wanted to first split the string (every elements of split iterator is some &str), you then need to simply apply map (because we want to take a single element - part of initial string, and output a single element - its extended version)
There's also one unpleasant obstacle - we'll also extend the last part with ", and", which is not quite what you want.
Finally we collect the iterator into a new String.
You worked with my_string_start by reference.
&my_string_start is of type &str and you can't collect it later. But split method automatically takes the String by reference, so no worries about accidentally moving your string out. You can safely remove this &, and the whole thing will be collected in the end into another String
fn main(){
let my_string_start = String::from("Hello, Goodmorning");
let augmented_string: String = my_string_start
.split(",")
.map(|f| format!("{}, and ", f))
.collect();
assert_eq!(augmented_string, String::from("Hello, and Goodmorning"));
}
EDIT
I tried to solve the issue of the trailing , and after the last element, and solving it with current capabilities of Rust is quite ugly and dirty. There's one unstable feature of Rust, that is available in Nightly version of the compiler, and it's just so good for your exact task, take a look.
intersperse takes an iterator, and inserts some element in between every element (not before and not after the iterator, only inside the iterator). With it you simply get what you wanted
#![feature(iter_intersperse)]
fn main(){
let my_string_start = String::from("Hello, Goodmorning");
let augmented_string: String = my_string_start
.split(",")
.intersperse(", and")
.collect();
assert_eq!(augmented_string, String::from("Hello, and Goodmorning"));
}
The following code fails to compile with:
55 | (":dataset_id", &dataset_id),
| ^^^^^^^^^^^ expected `u32`, found `i32`
pub fn save(&mut self, annotations: Vec<Record>, dataset_id: i32) -> Result<(), Error> {
let mut tx = self.conn.transaction()?;
for record in records {
let json: String = record();
let sql: &str =
"INSERT INTO records (record_id, dataset_id, value)
VALUES (:record_id, :dataset_id, :value)";
let mut statement = tx.prepare(sql)?;
statement.execute(&[
(":record_id", &record.id),
(":dataset_id", &dataset_id),
(":value", "hello world")]);
};
tx.commit()?;
Ok(())
}
And if I remove dataset_id from my SQL statement and comment out the line: (":dataset_id", &dataset_id),
Then it fails to compile with:
56 | (":value", &"hello".to_string()),
| ^^^^^^^^^^^^^^^^^^^^ expected `u32`, found struct `std::string::String`
The argument to execute is a P: Params, i.e. "any type that implements Params". The Rust compiler will not make a guess at what specific type you want and then process the argument accordingly. Instead, it will just resolve the type of the argument on its own and then see if it implements Params.
This is your argument:
&[(":record_id", &record.id),
(":dataset_id", &dataset_id),
(":value", "hello world")]
On its own, what is the type? It's a reference to an array literal containing three tuples: a (&str, &u32), a (&str, &i32), and a (&str, &str).
Lacking any further information, the compiler guesses that the first element is the canonical one, and thus tries to convert the others accordingly. Thus you get the "cannot convert &i32 to &u32" errors.
What you need, however, is an array of (&str, &dyn ToSql) tuples.
So you can do one of two things.
First, explicitly cast the first param value to the right type:
&[(":record_id", &record.id as &dyn ToSql),
(":dataset_id", &dataset_id),
(":value", "hello world")]
Or second, use the named_params! macro that rusqlite provides, which is arguably prettier:
statement.execute(named_params!{
":record_id": record.id,
":dataset_id": dataset_id,
":value": "hello world",
});
I playing with the Rust code, and I've got to a place in which I have a problem with converting Option<&[u8; 32]> to Option<&[u8]>.
A (very) simplified example:
pub type Foo = [u8; 32];
fn fun_one(inp: Option<&[u8]>) {
println!("{:?}", inp);
}
fn fun_two(x: Option<&Foo>) {
fun_one(x)
}
fn main() {
let x = [11u8; 32];
fun_two(Some(&x));
}
Link: Rust Playground
error[E0308]: mismatched types
--> src/main.rs:8:13
|
8 | fun_one(x)
| ^ expected slice `[u8]`, found array `[u8; 32]`
|
= note: expected enum `Option<&[u8]>`
found enum `Option<&[u8; 32]>`
A slice isn't just a pointer over an array. It's both the pointer to the data and a length (see Arrays and Slices) as it refers to only a part of the array. This is why the types aren't compatible.
What you want here is a slice over the whole array, which you get with the .. full range expression: slice = &array[..].
Having an option, you can conditionnaly apply this transformation using map.
Combining all this:
fn fun_two(x: Option<&Foo>) {
fun_one(x.map(|a| &a[..]))
}
I need to split a String (not &str) by another String:
use std::str::Split;
fn main() {
let x = "".to_string().split("".to_string());
}
Why do I get this error and how to avoid it if I already have to operate on strings?
error[E0277]: the trait bound `std::string::String: std::ops::FnMut<(char,)>` is not satisfied
--> src/main.rs:4:32
|
4 | let x = "".to_string().split("".to_string());
| ^^^^^ the trait `std::ops::FnMut<(char,)>` is not implemented for `std::string::String`
|
= note: required because of the requirements on the impl of `std::str::pattern::Pattern<'_>` for `std::string::String`
According to the #rust-beginners IRC channel, this might be an example of Deref failing in 1.20.0-nightly. How to split a string in Rust? doesn't address the problem of splitting by String, not &str.
All is in the documentation. You can provide one of:
A &str,
A char,
A closure,
Those three types implement the Pattern trait. You are giving a String to split instead of a &str.
Example:
fn main() {
let x = "".to_string();
let split = x.split("");
}
I talked about this with #rust-beginners IRC channel and heard the following:
15:12:15 achird | d33tah: split accepts a Pattern, where Pattern can be &str or char, but you're passing a String (no idea why deref is not working)
15:13:01 d33tah | achird: thanks! how can I convert string to str?
15:13:03 achird | i think a simple split(&delimiter2) should fix the problem
15:16:26 calops | why isn't deref working though?
15:21:33 #mbrubeck | calops, d33tah: Deref coercions only work if one exact "expected" type is known. For a generic type like <P: Pattern>, coercion doesn't kick in.
15:24:11 #mbrubeck | d33tah: The error should definitely be improved... It should complain that `String` doesn't impl `Pattern`, instead of jumping straight to `FnMut(char)`
So basically, the solution is to add & before the delimiter string, like this:
fn main() {
let s1 = "".to_string();
let s2 = "".to_string();
let x = s1.split(&s2);
}
Editor's note: This question was asked before Rust 1.0. Since then, many functions and types have changed, as have certain language semantics. The code in the question is no longer valid, but the ideas expressed in the answers may be.
I'm trying to list the files in a directory and copy the filename to my own Vec. I've tried several solutions, but it always ends up with a problem of not being able to create long enough living variables. I don't understand my mistake.
fn getList(action_dir_path : &str) -> Vec<&str> {
let v = fs::readdir(&Path::new(action_dir_path))
.unwrap()
.iter()
.map(|&x| x.filestem_str().unwrap())
.collect();
return v;
}
Why does the compiler complain about "x" ? I don't care about x, I want the &str inside it and I thought &str were static.
I tried this way, but I got the same result with the compiler complaining about "paths" not living long enough.
fn getList2(action_dir_path : &str) -> Vec<&str> {
let paths = fs::readdir(&Path::new(action_dir_path)).unwrap();
let mut v : Vec<&str> = Vec::new();
for path in paths.iter(){
let aSlice = path.filestem_str().unwrap();
v.push(aSlice);
}
return v;
}
Here is the playground.
The most literal translation of your code that supports Rust 1.0 is this:
use std::{fs, path::Path, ffi::OsStr};
fn getList(action_dir_path: &str) -> Vec<&OsStr> {
let v = fs::read_dir(&Path::new(action_dir_path))
.unwrap()
.map(|x| x.unwrap().path().file_stem().unwrap())
.collect();
return v;
}
This produces the error messages:
Rust 2015
error[E0597]: borrowed value does not live long enough
--> src/lib.rs:6:18
|
6 | .map(|x| x.unwrap().path().file_stem().unwrap())
| ^^^^^^^^^^^^^^^^^ - temporary value only lives until here
| |
| temporary value does not live long enough
|
note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 3:1...
--> src/lib.rs:3:1
|
3 | / fn getList(action_dir_path: &str) -> Vec<&OsStr> {
4 | | let v = fs::read_dir(&Path::new(action_dir_path))
5 | | .unwrap()
6 | | .map(|x| x.unwrap().path().file_stem().unwrap())
7 | | .collect();
8 | | return v;
9 | | }
| |_^
Rust 2018
error[E0515]: cannot return value referencing temporary value
--> src/lib.rs:6:18
|
6 | .map(|x| x.unwrap().path().file_stem().unwrap())
| -----------------^^^^^^^^^^^^^^^^^^^^^
| |
| returns a value referencing data owned by the current function
| temporary value created here
The problem comes from Path::file_stem. This is the signature:
pub fn file_stem(&self) -> Option<&OsStr>
This indicates that the method will return a borrowed reference to a OsStr. The PathBuf struct is the owner of the string. When you leave the method, there's nowhere left that owns the PathBuf, so it will be dropped. This means that any references into the PathBuf will no longer be valid. This is Rust preventing you from having references to memory that is no longer allocated, yay for Rust!
The easiest thing you can do is return a Vec<String>. String owns the string inside of it, so we don't need to worry about it being freed when we leave the function:
fn get_list(action_dir_path: &str) -> Vec<String> {
fs::read_dir(action_dir_path)
.unwrap()
.map(|x| {
x.unwrap()
.path()
.file_stem()
.unwrap()
.to_str()
.unwrap()
.to_string()
})
.collect()
}
I also updated the style (at no charge!) to be more Rust-like:
Use snake_case for items
No space before the colon in type definitions
There's no reason to set a variable just to return it.
Don't use explicit return statements unless you are exiting from a function early.
There's no need to wrap the path in a Path.
However, I'm not a fan of all of the unwrapping. I'd write the function like this:
use std::{ffi::OsString, fs, io, path::Path};
fn get_list(action_dir_path: impl AsRef<Path>) -> io::Result<Vec<OsString>> {
fs::read_dir(action_dir_path)?
.map(|entry| entry.map(|e| e.file_name()))
.collect()
}
fn main() {
println!("{:?}", get_list("/etc"));
}
In addition to the changes above:
I use a generic type for the input path.
I return a Result to propagate errors to the caller.
I directly ask the DirEntry for the filename.
I leave the type as an OsString.
One small related point:
I thought &str were static.
&'static strs are static, but that's only one kind of &str. It can have any kind of lifetime.