参照型とポインタ型

参照型とポインタ型

  • 参照型 &T / &mut T - ある所有された値への借用を表す型
  • ポインタ型 *const T / *mut T - nullを表すことができる

ポインタ型を使うメリットとデメリット

Rustではポインタ型は、値がnullの可能性がある。 したがって扱う場合は、unsafeブロック内部で行う必要がある。 その代わり、Cポインタのように配列の走査が行える。

ポインタ型

変数宣言時に、初期化せずに、nullを代入することが出来る。 ただし、参照外し(*演算子を使って)がされた場合は、非nullで、かつ初期化された値を返す必要がある。

fn main() {
    let p: *const u8;
    unsafe {
        p = "ABC".as_ptr();
        println!("{:?}", *p as char); // u8からcharにキャスト
        println!("{:?}", *p.add(1) as char);
        println!("{:?}", *p.add(2) as char);
    }
}
  • *p: 参照外しに寄ってu8に変換

ポインタ型の作り方

1. 参照強制(&T or &mut T)による生成

let my_num: i32 = 10;
let ptr_my_num: *const i32 = &my_num; // アノテーションを付ける必要がある

let mut my_speed: i32 = 88;
let ptr_my_speed: *mut i32 = &mut my_speed;

NOTE:

2. Box型から値を取り出して生成

let my_num: Box<i32> = Box::new(10);
let p_my_num: *const i32 = &*my_num;

NOTE:

  • Box型はスマートポインタなので、*演算子(参照外し演算子) によって内部の値を取り出せる
  • &演算子によってポインタ型に変換している

3. Cから取得する

extern crate libc;

use std::mem;

unsafe {
    let my_num: *mut i32 = libc::malloc(mem::size_of::<i32>()) as *mut i32;
    if my_num.is_null() {
        panic!("failed to allocate memory");
    }
    libc::free(my_num as *mut libc::c_void);
}

一般的に、 Rustからmalloc,freeを直接使わない。 しかし、C APIはたくさんのポインタを手渡しするため、Rustにおいてもポインタとして扱う。

ポインタ型の使い方

配列走査などをポインタ型で行うと、処理速度の向上が見込めるが、nullもしくは未初期化やオーバーフローしている可能性があるため、unsafeブロック内部で処理を記述する必要がある。

値の取得

fn main() {
    let s: &str = "ABC";
    let ptr: *const u8 = s.as_ptr();

    unsafe {
        println!("{}", *ptr); // 先頭ポインタ(u8)の値を指し示す。型はu8。
        println!("{}", *ptr.offset(0) as char); // A
        println!("{}", *ptr.offset(1) as char); // B
    }
}
  • *演算子
    • ポインタが指し示している先頭の値を指し示す
    • unsafeブロック内部で使用する必要がある
  • offset(self, count: isize)
    • ポインタからのオフセットを計算する
    • countの単位は、size_of::<T>()バイト毎になる
    • コンビニエンスメソッドとして、add(count: usize) , sub(count: usize)が存在する
let s: &str = "ABC";
unsafe {
    let end: *const u8 = s.as_ptr().add(3); 
    println!("{}", *end as char); // LF(0xA)を指し示している
    println!("{}", *end.sub(1) as char); // C
    println!("{}", *end.sub(2) as char); // B
}
  • addに指定するcountによっては、オーバーフローしている可能性がある

値の走査

文字の検索

fn main() {
    let text = "A language empowering everyone to build reliable and efficient software.";
    let word = "everyone";

    let p_text: *const u8 = text.as_ptr();
    let p_word: *const u8 = word.as_ptr();
    let mut exist = false;
    'i: for i in 0..text.len() {
        'j: for j in 0..word.len() {
            unsafe {
                if *p_text.add(i + j)  != *p_word.add(j) {
                    break 'j;
                }
            }
            if j == word.len() - 1 {
                exist = true;
                break 'i;
            }
        }
    }
    println!("result = {}", exist);
}

関連

スマートポインタ https://yossan.hatenablog.com/entry/2020/09/20/224329

Dynamically Sized Type (DST) https://yossan.hatenablog.com/entry/2020/10/11/230422

参照

Primitive Type pointer https://doc.rust-lang.org/std/primitive.pointer.html