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
}
}
}