Reputation: 9465
I need to check if a given &str
is a sub-slice of another &str
, but I don't know what is the best way to do it. I wrote a function which does the job by using str::as_ptr()
and pointer::offset_from()
, but it has safety issues and I don't know how to handle them:
fn is_part_of(outer: &str, part: &str) -> bool {
// SAFETY: not handled
let offset = unsafe { part.as_ptr().offset_from(outer.as_ptr()) };
offset >= 0 && offset + (part.len() as isize) <= outer.len() as isize
}
fn main() {
let hello = "hello";
// Works as expected:
println!("{:?}", is_part_of(hello, &hello[1..5])); // true
// Works as expected, but as far as I understand, this is undefined behaviour:
println!("{:?}", is_part_of(hello, "ello")); // false
}
In the Rust doc for pointer::offset_from(), it is mentionned:
Both pointers must be derived from a pointer to the same object.
To my understanding, this is exactly what I don't want in my case.
is_part_of()
above in a clean and safe way ?&str
is a sub-slice of another &str
?I want to write a struct SubStr
which contains a sub-slice of a bigger &str
, and a reference to this bigger &str
:
#[derive(Debug)]
pub struct SubStr<'src> {
source: &'src str,
part: &'src str, // MUST be a sub-slice of self.source
}
impl<'src> SubStr<'src> {
pub fn new(source: &'src str, part: &'src str) -> Self {
// Here, an assertion is missing, which would ensure that `part` is a sub-slice
// of `source`.
Self { source, part }
}
}
I need to keep track of the bigger string (self.source
) because I want to be able to extend self.part
by looking to what is remaining before/after its left/right boundaries, in self.source
.
I.e., I want to implement such methods:
impl<'src> SubStr<'src> {
/// If we have:
/// self.source = "Hello foo world"
/// self.part = &self.source[10..15] (==> "world")
/// then updates `self.part` to `&self.source[6..15]` (==> "foo world")
pub fn extend_to_one_word_left(&mut self) {
self.part = unimplemented!();
}
/// If we have:
/// self.source = "Hello foo world"
/// self.part = &self.source[6..9] (==> "foo")
/// then updates `self.part` to `&self.source[5..10]` (==> " foo ")
pub fn untrim_spaces(&mut self) {
self.part = unimplemented!();
}
}
Upvotes: 3
Views: 471
Reputation: 155495
Instead of pointer::offset_from()
, you can convert the pointer to usize
and use usize
arithmetic, which is entirely safe:
fn is_part_of(outer: &str, part: &str) -> bool {
let outer_beg = outer.as_ptr() as usize;
let outer_end = outer_beg + outer.len();
let part_beg = part.as_ptr() as usize;
let part_end = part_beg + part.len();
part_beg >= outer_beg && part_end <= outer_end
}
Upvotes: 4