I've learnt Rust a few months, but I occasionally come across the puzzle about this:
// This fails
use std::marker::PhantomData;
fn main() {
let s = S { s: std::str::from_utf8(&[65]).unwrap(), };
println!("{:?}", s);
let yield_s = YieldS { vec: (65..70).collect(),
pos: 0,
pht: PhantomData, };
yield_s.map(|S { s }| println!("{}", s)).last();
}
#[derive(Debug, Clone)]
struct S<'a> {
s: &'a str,
}
struct YieldS<'a> {
vec: Vec<u8>,
pos: usize,
pht: PhantomData<&'a str>,
}
impl<'a> Iterator for YieldS<'a> {
type Item = S<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.pos < self.vec.len() {
self.pos += 1;
Some(Self::from_bytes(&self.vec[self.pos - 1..self.pos]))
} else {
None
}
}
}
impl<'a> YieldS<'a> {
pub fn from_bytes(b: &'a [u8]) -> S { S { s: std::str::from_utf8(b).unwrap(), } }
}
The code above fails with:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to co
nflicting requirements
--> src/main.rs:36:36
|
36 | Some(Self::from_bytes(&self.vec[self.pos - 1..self.pos]))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime defined on the method body at 33:13.
..
--> src/main.rs:33:13
|
33 | fn next(&mut self) -> Option<Self::Item> {
| ^^^^^^^^^
note: ...so that reference does not outlive borrowed content
--> src/main.rs:36:36
|
36 | Some(Self::from_bytes(&self.vec[self.pos - 1..self.pos]))
| ^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 30:6...
--> src/main.rs:30:6
|
30 | impl<'a> Iterator for YieldS<'a> {
| ^^
note: ...so that the types are compatible
--> src/main.rs:36:18
|
36 | Some(Self::from_bytes(&self.vec[self.pos - 1..self.pos]))
| ^^^^^^^^^^^^^^^^
= note: expected `YieldS<'_>`
found `YieldS<'a>`
For more information about this error, try `rustc --explain E0495`.
error: could not compile `rustdx` due to previous error
I know the owned type String
solves the issue with slight overheads. But I'm not sure whether I'm missing something...
// This works
fn main() {
let s = Sowned { s: std::str::from_utf8(&[65]).unwrap().into(), };
println!("{:?}", s);
let yield_sowned = YieldSowned { vec: (65..70).collect(),
pos: 0, };
let res = yield_sowned.enumerate()
.fold("".into(), |acc, (i, Sowned { s })| format!("{}\n{}: {}", acc, i, s));
println!("{}", res);
}
#[derive(Debug, Clone)]
struct Sowned {
s: String,
}
struct YieldSowned {
vec: Vec<u8>,
pos: usize,
}
impl Iterator for YieldSowned {
type Item = Sowned;
fn next(&mut self) -> Option<Self::Item> {
if self.pos < self.vec.len() {
self.pos += 1;
Some(Self::from_bytes(&self.vec[self.pos - 1..self.pos]))
} else {
None
}
}
}
impl YieldSowned {
pub fn from_bytes(b: &[u8]) -> Sowned { Sowned { s: std::str::from_utf8(b).unwrap().into(), } }
}
The full code on playground.
I incline to use &str
whenever the modification on utf8 bytes is never needed.
But now I just feel so upset &str
can't work as expected.
BTW. .fold("".into(), |acc, (i, Sowned { s })| format!("{}\n{}: {}", acc, i, s))
seems to reallocate many times. Is this true?
If true, how can we trust Rust to perform efficiently? If the iterator yields thousands of times, the format!
reallocates exact thousands of times???
As far as I know, String
or Vec
with variable length is likely to reallocate many times, but what about that with fixed length like "a long str... maybe thousands of bytes".to_string()
does allocate once?
When, personally speaking, could I use String
fearlessly...