Public Theta Blog

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