Rustで文字列スライスのポインタ間の距離を測る
pub fn distance_ptr(a: &str, b: &str) -> usize {
(b.as_ptr() as usize) - (a.as_ptr() as usize)
}
コンパイルすると:
movq %rdx, %rax
subq %rdi, %rax
retq
補足
これを使うと、ある文字列(source
)に対してその部分文字列(span
)がどの位置や範囲にあるかを、追加情報なしに取得することができるはず(安全な操作かどうかは正直なところ分からない)。
なお_unwrap
のついている関数では、span
として与えられた文字列スライスが本当にsource
の範囲内にあるかを確認してOption
で返すようになっている。
fn position_ptr_unwrap(source: &str, span: &str) -> usize {
let source_start = source.as_ptr() as usize;
let span_start = span.as_ptr() as usize;
span_start - source_start
}
fn position_ptr(source: &str, span: &str) -> Option<usize> {
let source_start = source.as_ptr() as usize;
let span_start = span.as_ptr() as usize;
if span_start < source_start {
return None;
}
let source_end = source_start.checked_add(source.len())?;
let span_end = span_start.checked_add(span.len())?;
if source_end < span_end {
return None;
}
Some(span_start - source_start)
}
fn range_ptr_unwrap(source: &str, span: &str) -> core::ops::Range<usize> {
let source_start = source.as_ptr() as usize;
let span_start = span.as_ptr() as usize;
let range_start = span_start - source_start;
range_start..(range_start + span.len())
}
fn range_ptr(source: &str, span: &str) -> Option<core::ops::Range<usize>> {
let source_start = source.as_ptr() as usize;
let span_start = span.as_ptr() as usize;
if span_start < source_start {
return None;
}
let source_end = source_start.checked_add(source.len())?;
let span_end = span_start.checked_add(span.len())?;
if source_end < span_end {
return None;
}
let range_start = span_start - source_start;
let range = range_start..(range_start + span.len());
Some(range)
}