Can anyone explain to me how the |slice| in this statement work?
Source (l35): https://gist.github.com/madhavanmalolan/b30b47640449f92ea00e4075d63460a6
let amount = rest_of_data
.get(..8)
.and_then(|slice| slice.try_into().ok())
.map(u64::from_le_bytes)
.unwrap();
This code tries to transform a byte array to a u64.
let amount = rest_of_data // &[u8]
.get(..8) // Option<&[u8]>
.and_then(|slice /* &[u8] */ | slice.try_into().ok()) // Option<[u8; _]>
.map(u64::from_le_bytes) // Option<u64>
.unwrap();
rest_of_data is a reference to a [u8]. get(..8) method tries to get the first eight elements of the rest_of_data, returns an Option<&[u8]> (Some if there are at least eight elements present in the slice and None if there are fewer.)
As for your question;
To transform some bytes to u64 using u64::from_le_bytes, your input needs to be an owned value, not a reference. By calling .and_then(f) on Option<&[u8]> you give the closure f the val in Some(val) in this case it's &[u8]. Closure f takes one argument (named slice in this example) and returns the closure result. Then this returned value is fed to u64::from_le_bytes with .map() method.
Since u64::from_le_bytes takes an owned value, you need to transform the byte slice into an owned type, which is what the closure does with the .try_into() function. It gives you a byte array.
Related
i need split String "fooo:3333#baaar:22222"
Firstly by # secondary by :
and result must be <Vec<Vec<&str, i64>>>
for the first step (split by #) i came up with
.split('#').collect::<Vec<&str>>()
but I can't think of a solution for the second step
A Vec<&str, i64> is not a thing, so I assume you meant (&str, i64)
You can create that by splitting first, then mapping over the chunks.
let v = s
.split('#') // split first time
// "map" over the chunks and only take those where
// the conversion to i64 worked
.filter_map(|c| {
// split once returns an `Option<(&str, &str)>`
c.split_once(':')
// so we use `and_then` and return another `Option`
// when the conversion worked (`.ok()` converts the `Result` to an `Option`)
.and_then(|(l, r)| r.parse().ok().map(|r| (l, r)))
})
.collect::<Vec<(&str, i64)>>();
References:
https://doc.rust-lang.org/std/primitive.str.html#method.split
https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.filter_map
https://doc.rust-lang.org/core/primitive.str.html#method.split_once
https://doc.rust-lang.org/core/option/enum.Option.html#method.and_then
https://doc.rust-lang.org/std/primitive.str.html#method.parse
https://doc.rust-lang.org/std/result/enum.Result.html#method.ok
You can call a closure on each element of an iterator returned by the first split and split inside closure and push values to the vector.
let mut out = Vec::new();
s.split('#').for_each(|x| out.push(x.split(":").collect::<Vec<&str>>()));
I want to change a String into a vector of bytes and also modify its value, I have looked up and find How do I convert a string into a vector of bytes in rust?
but this can only get a reference and I cannot modify the vector. I want a to be 0, b to be 1 and so on, so after changing it into bytes I also need to subtract 97. Here is my attempt:
fn main() {
let s: String = "abcdefg".to_string();
let mut vec = (s.as_bytes()).clone();
println!("{:?}", vec);
for i in 0..vec.len() {
vec[i] -= 97;
}
println!("{:?}", vec);
}
but the compiler says
error[E0594]: cannot assign to `vec[_]`, which is behind a `&` reference
Can anyone help me to fix this?
You could get a Vec<u8> out of the String with the into_bytes method. An even better way, though, may be to iterate over the String's bytes with the bytes method, do the maths on the fly, and then collect the result:
fn main() {
let s = "abcdefg";
let vec: Vec<u8> = s.bytes().map(|b| b - b'a').collect();
println!("{:?}", vec); // [0, 1, 2, 3, 4, 5, 6]
}
But as #SvenMarnach correctly points out, this won't re-use s's buffer but allocate a new one. So, unless you need s again, the into_bytes method will be more efficient.
Strings in Rust are encoded in UTF-8. The (safe) interface of the String type enforces that the underlying buffer always is valid UTF-8, so it can't allow direct arbitrary byte modifications. However, you can convert a String into a Vec<u8> using the into_bytes() mehod. You can then modify the vector, and potentially convert it back to a string using String::from_utf8() if desired. The last step will verify that the buffer still is vaid UTF-8, and will fail if it isn't.
Instead of modifying the bytes of the string, you could also consider modifying the characters, which are potentially encoded by multiple bytes in the UTF-8 encoding. You can iterate over the characters of the string using the chars() method, convert each character to whatever you want, and then collect into a new string, or alternatively into a vector of integers, depending on your needs.
To understand what's going on, check the type of the vec variable. If you don't have an IDE/editor that can display the type to you, you can do this:
let mut vec: () = (s.as_bytes()).clone();
The resulting error message is explanative:
3 | let mut vec: () = (s.as_bytes()).clone();
| -- ^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `&[u8]`
So, what's happening is that the .clone() simply cloned the reference returned by as_bytes(), and didn't create a Vec<u8> from the &[u8]. In general, you can use .to_owned() in this kind of case, but in this specific case, using .into_bytes() on the String is best.
I have a vector of Strings as in the example below, and for every element in that vector, I want to get the second and third items. I don't know if I should be collecting a &str or String, but I haven't gotten to that part because this does not compile.
Everything is "fine" until I add the slicing [1..]
let elements: Vec<&str> = vec!["foo\tbar\tbaz", "ffoo\tbbar\tbbaz"]
.iter()
.map(|rec| rec.rsplit('\t').collect::<Vec<_>>()[1..])
.collect();
It complains because
the size for values of type `[&str]` cannot be known at compilation time
the trait `std::marker::Sized` is not implemented for `[&str]`rustcE0277
As the compiler tells you, the slicing is broken because in Rust a slice returns, well, the slice. Whose size is unknown at compile-time (hence the compiler complaining that it's unsized).
That's why you normally reference the slice e.g.
&thing[1..]
unless it's a context where it doesn't matter. Or you immediately convert the slice to a vector or array.
However here it would not work, because a slice is a "borrowing" structure, it doesn't own anything. And it borrows the Vec being created inside the map, which means you'll get a borrowing error, because the Vec will be destroyed at the end of the callback, and thus the slice would be referencing invalid memory:
error[E0515]: cannot return value referencing temporary value
--> src/main.rs:5:16
|
5 | .map(|rec| &rec.rsplit('\t').collect::<Vec<_>>()[1..])
| ^------------------------------------^^^^^
| ||
| |temporary value created here
| returns a value referencing data owned by the current function
The solution is to filter the iterator before collecting the vec, using Iterator::skip:
let elements: Vec<&str> = my_vec
.iter()
.map(|rec| rec.rsplit('\t').skip(1).collect::<Vec<_>>())
.collect();
However this means you now have an Iterator<Item=Vec<&str>>, which doesn't collect to a Vec<&str>.
You could always Iterator::flatten the inner vecs, but in reality they're completely unnecessary: you can just Iterator::flat_map each original string into a stream of strings which automatically get folded into the parent:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=f2c33c1b6a30224202357dc4bd5c1d19
let my_vec = vec!["foo\tbar\tbaz", "ffoo\tbbar\tbbaz"];
let elements: Vec<&str> = my_vec
.iter()
.flat_map(|rec| rec.rsplit('\t').skip(1))
.collect();
dbg!(elements);
By the by, the code you're showing doesn't match the description, you say:
for every element in that vector, I want to get the second and third items
but since you're using rsplit what you're getting is the second and first: rsplit will iterate from the end, hence the r for reverse.
here I am splitting vec into sub-vectors of equal size 4 and then returning a collection, I want the type returned from the collection to be Vec<String>. How can I do so?
let mut split_operations : Vec<String> = vec[2..].chunks(4).collect::<String>();
This generates the following error:
a value of type std::string::String cannot be built from an iterator over elements of type &[std::string::String]
value of type std::string::String cannot be built from std::iter::Iterator<Item=&[std::string::String]>
help: the trait std::iter::FromIterator<&[std::string::String]> is not implemented for std::string::Stringrustc(E0277)
The type parameter of collect() is what the entire thing should be collected to. So the parameter to collect and the actual type are redundant, only one is needed.
However Rust doesn't want to guess how or what to the chunks are supposed to change: here you have an iterator of slices (Iterator<Item=&[String]). You can collect an Iterator<Item=String> to a Vec<String> or to a single String (it'll join all the items), but the intermediate slice means Rust's got no idea what's supposed to happen.
If you want each chunk to become a single string, you need to do that explicitely in a map e.g.
let mut split_operations : Vec<String> = vec[2..].chunks(4)
.map(|c| c.concat())
.collect();
If you want to play (and / or infuriate your colleagues), you can also write the map callback as <[_]>::concat.
As per rust doc, string slice is:
This string is statically allocated, meaning that it's saved inside our compiled program, and exists for the entire duration it runs.
What about vector slices? Are these bytecode hardcoded values?
Rust doc does not explain properly what vector slice is:
Similar to &str, a slice is a reference to another array. We can get a slice from a vector by using the as_slice() method:
let vec = vec![1i, 2i, 3i];
let slice = vec.as_slice();
Actually... it is not always statically allocated. Note the specific wording:
let string = "Hello there.";
This string is statically allocated, meaning that it's saved inside our compiled program, and exists for the entire duration it runs (empahsis mine).
In the case of a string literal, the string is statically allocated, and the string slice refers to it. However there are other cases of string slices where they refer to dynamically allocated strings.
In general, a slice is a contiguous sequence of elements in an array (wherever that array lives). Therefore, in Rust:
a string slice is a slice of bytes in a UTF-8 encoded array
a vector slice is a slice of elements (of type T) in an array of type Ts
If the array has a static lifetime (ie, exists for the whole duration of the program), then this is represented with the static keyword:
&'static str is a &str (string slice) with such a lifetime, it is thus the precise types of string literals in Rust.
Otherwise, the lifetime is generally bounded to that of the array owner, for example:
let vec = vec!(1i, 2, 3); // create a dynamically allocated array of 3 `int`
let slice = vec.as_slice(); // create a slice into this array,
// the lifetime of which cannot (obviously)
// exceed that of the array.
thus if vec has a lifetime 'a, then slice has for type &'a [int] as can be seen in the type signature:
fn as_slice(&'a self) -> &'a [T]
(from the std::vec::Vec page)