ジェネリック境界(@Rust)

ジェネリクスにおいてジェネリクスの型に制限を与えることですが、

https://doc.rust-jp.rs/rust-by-example-ja/generics/bounds.html

のコードから変更してtriangleの処理もできるように変更しています。あと三角形の面積の計算の仕方が変なのでそこも変更。

ここではジェネリクスにtraitのHasArea型を満たすことという条件で「境界」を決めています。

またprint_debugに引き渡す引数にはDebugが実装されていなければならないので、struct TriangleとRectangleにはDebug実装のための(#[derive(Debug)])が必須です。

// A trait which implements the print marker: `{:?}`.
use std::fmt::Debug;

trait HasArea {
    fn area(&self) -> f64;
}

impl HasArea for Rectangle {
    fn area(&self) -> f64 {
        self.length * self.height
    }
}

impl HasArea for Triangle {
    fn area(&self) -> f64 {
        (self.length * self.height) / 2.0
    }
}

#[derive(Debug)]
struct Rectangle {
    length: f64,
    height: f64,
}

#[derive(Debug)]
struct Triangle {
    length: f64,
    height: f64,
}

// The generic `T` must implement `Debug`. Regardless
// of the type, this will work properly.
fn print_debug(t: &T) {
    println!("{:?}", t);
}

// `T` must implement `HasArea`. Any type which meets
// the bound can access `HasArea`'s function `area`.
fn area<t>(t: &T) -> f64
where
    T: HasArea,
{
    t.area()
}

fn main() {
    let rectangle = Rectangle {
        length: 3.0,
        height: 4.0,
    };
    let triangle = Triangle {
        length: 3.0,
        height: 5.0,
    };

    print_debug(&rectangle);
    println!("Area: {}", area(&rectangle));

    print_debug(&triangle);
    println!("Area: {}", area(&triangle));
}

 

admin

 

スマートポインターの独自実装(@Rust)

Box<T>のようなスマートポインターの独自実装がRustドキュメントの中に出てきます。Box<T>は一番基本的なスマートポインターで他にもいくつか存在しますが、

https://doc.rust-jp.rs/book-ja/ch15-02-deref.html

もともとスマートポインターはRust固有ではなくC++でもありますが、定義は『通常のポインタのように振る舞うだけでなく、追加のメタデータと能力があるデータ構造』とありますが、追加のメタデータと能力とは以下のサンプルコードで示される参照外しは一例かと思います。もともとC++におけるスマートポインターも目的とするところはほぼ同じ。

以下のコードはリンクからのコードですが、独自のMyBox(Box相当)でBoxのメソッドの一つであるnew()を実装すれば、引数の値をヒープエリアに割り当ててくれます。ただし、DerefをMyBoxメソッドに対して実装しないとせっかくMyBoxで割り当てたメモリエリアの参照ができないので、以下のコードはコンパイルできません。

hello関数と呼出の例は、もしMyBoxにDerefが実装されていないと呼び出しが煩雑になるよという例を示すためで、もしDeref実装がないと以下のような読みづらいコードを書かないといけないようです。

hello(&m); –> hello(&(*m)[..]);

これは、

https://qiita.com/moyamoyac/items/5aea471d6676625dcd62

で記述されているように、以下の三段階の変換が組み込まれたコードなのでぱっと見分かりづらい。

mMyBox<String>
*mString
&(*m)[..]&str

 

use std::ops::Deref;

struct MyBox<T>(T);

impl MyBox<T> {      // original smart pointer
    fn new(x: T) -> MyBox {
        MyBox(x)
    }
}

impl Deref for MyBox {    // deref coercion
    type Target = T;    // to define return value type T for the Deref "Target"
                        // in the DeRef trait : fn deref(&self) -> &Self::Target;

    fn deref(&self) -> &T {
        &self.0
    }
}

fn hello(name: &str) {          // "&" is needed to get a known size of the argument at compile-time
    println!("Hello, {}!", name);
}

fn main() {
    let x = 5;
    let y = MyBox::new(x);

    assert_eq!(5, x);
    assert_eq!(5, *y);  // to success compile, Deref definition should be defined

    println!("x = {}, y = {}", x, *y);

    let m = MyBox::new(String::from("Rust"));
    hello(&m);
}

 

admin

関数呼び出しと戻り値がクロージャーの場合(@Rust)

https://kudohamu.hatenablog.com/entry/2015/09/26/084621

で、以下のようなコードが出てきて、なぜhoge()()と()が二重になるのかなと思いましたが、

fn main(){
    println!("{}", hoge()());
}

fn hoge() -> Box<dyn Fn() -> String> {
    Box::new(|| -> String {
        String::from("ほげー!")
    })
}

Geminiで質問すると、

hoge()だけだと関数呼び出すだけで、hoge()()としないと戻り値であるクロージャーの実行はできないと以下のように言われました。

まとめ:

hoge()() は、hoge 関数を呼び出し、その結果として得られたクロージャを呼び出すという二つの操作をまとめて実行しています。

「関数の戻り値としてのクロージャーを実行するなら二重括弧を使う」ということのようです

したがって、以下のように冗長に記述もできます。

    let abc = hoge();
    println!("{}", abc());

同じことは、クロージャーを戻り値にしている

https://isehara-3lv.sakura.ne.jp/blog/2024/04/05/クロージャーを戻り値にするrust/

のコードでも言えて、

    let fn_plain = create_fn(); // return a closure
    
    fn_plain();

と

    create_fn()();

は等価です

 

admin

Iterator::anyとfind

https://doc.rust-jp.rs/rust-by-example-ja/fn/closures/closure_examples/iter_any.html

と次のページに出てくるanyとfindですが、トレイトの型は以下のようになっていますが、findの&Self::Itemはクロージャが変数を参照として取ってくるので、クロージャーでの表現は二重参照の形になります。

<any/findのクレート表現>

    fn any<F>(&mut self, f: F) -> bool
    where
        Self: Sized,
        F: FnMut(Self::Item) -> bool,


    fn find<P>(&mut self, predicate: P) -> Option<self::item> 
    where 
        Self: Sized, 
        P: FnMut(&Self::Item) -> bool,  // &Self::Item means closure variables are used as reference  

以上の前提なので、

https://doc.rust-jp.rs/rust-by-example-ja/fn/closures/closure_examples/iter_find.html

からのソースコードでは&x -> &&x、x -> &xのように参照記号(&)が追加されています。

fn main() {
    let vec1 = vec![1, 2, 3];
    let vec2 = vec![4, 5, 6];

    // `iter()` for vecs yields `&i32`.
    let mut iter = vec1.iter();
    // `into_iter()` for vecs yields `i32`.
    let mut into_iter = vec2.into_iter();

    // `iter()` for vecs yields `&i32`, and we want to reference one of its
    // items, so we have to destructure `&&i32` to `i32`
    println!("Find 2 in vec1: {:?}", iter     .find(|&&x| x == 2));
    // `into_iter()` for vecs yields `i32`, and we want to reference one of
    // its items, so we have to destructure `&i32` to `i32`
    println!("Find 2 in vec2: {:?}", into_iter.find(| &x| x == 2));

    let array1 = [1, 2, 3];
    let array2 = [4, 5, 6];

    // `iter()` for arrays yields `&i32`
    println!("Find 2 in array1: {:?}", array1.iter()     .find(|&&x| x == 2));
    // `into_iter()` for arrays yields `i32`
    println!("Find 2 in array2: {:?}", array2.into_iter().find(|&x| x == 2));
}

 

admin

クロージャーを関数の戻り値にする(@rust)

クロージャーを関数の戻り値にするときにも、Fn/FnMut/FnOnce指定が必要です。なぜならクロージャーの受け取りと同じくクロージャーの型は不明なので、

さらに関数の戻り値とするためにはクロージャーに渡される変数をmoveで参照では無く値渡しにしないといけません。以下の例でdataはクロージャーに渡しても戻り値では無い(参照が無効になることはない)ので、ここでのmoveはあっても無くても、値渡しだと時間が余計にかかるだけで、結果は同じですが、create_fn()中のクロージャーにはmoveがないとtextが参照では関数の戻りで参照無効となるのでコンパイルが通りません。

// return a closure
fn create_fn() -> impl Fn() {
    let text = "Fn".to_owned();
    move || println!("This is a: {}", text)
}

fn main() {
    // return a closure
    let data = vec![1, 2, 3];

    // you don't need 'move' in this case becuase data is still available after this line
    let closure = move || println!("captured {data:?} by value");   
    closure();

    // call function and receive closure
    let fn_plain = create_fn(); // return a closure
    
    fn_plain();

 

admin

 

関数に関数を渡す(@Rust)

関数の引数としてクロージャーを渡せるのと同様に関数も引数として渡すことができます。

以下がサンプルのコードですが、ここではクロージャー(sqr)と関数(sqr_f)とそれらの引数も同時に関数(apply_clo())に渡しています。

当然トレイト境界と引数がクロージャーと同じ関数(ここではFn(i32))でないと渡せませんが、


fn apply_clo<F: Fn(i32)>(i: i32, f: F) {
    f(i);
}

fn main() {
    // ARG for the closure
    let sqr = |y: i32| println!("{}", y*y);

    // equivalent function
    fn sqr_f(y: i32){
        println!("{}", y*y);
    }

    apply_clo(23, sqr);
    apply_clo(32, sqr_f);
}

 

admin

 

クロージャーとFn/FnMut/FnOnceの使い分け(@Rust)

これもRustのメモリ管理のための特有機能になりますが、クロージャーを使う時にはその引数は、匿名構造体に保存されるようで、クロージャーを受け取るときにその引数がどういう使われ方をするかを指定するためにFn/FnMut/FnOnceの三種類のトレイトが存在します。

Fn/FnMut/FnOnceについての解説は以下のリンクが分かりやすい。

https://qiita.com/hiratasa/items/c1735dc4c7c78b0b55e9

Rust by exampleの以下のリンクから、

https://doc.rust-jp.rs/rust-by-example-ja/fn/closures/input_parameters.html

コードを抜き出すと以下のようになりますが、ここでfn apply()とfn apply_to_3()はお互いに独立しています。

fn apply_to_3()はトレイト境界がシンプルで中で何らの変更もしていないのでFn指定になっています。

// A function which takes a closure as an argument and calls it.
// <f> denotes that F is a "Generic type parameter"
fn apply<f>(f: F) where
    // The closure takes no input and returns nothing.
    F: FnOnce() {
    // ^ TODO: Try changing this to `Fn` or `FnMut`.

    f();
}

// A function which takes a closure and returns an `i32`.
fn apply_to_3<f>(f: F) -> i32 where
    // The closure takes an `i32` and returns an `i32`.
    F: Fn(i32) -> i32 {

    f(3)
}

fn main() {
    use std::mem;

    let greeting = "hello";
    // A non-copy type.
    // `to_owned` creates owned data from borrowed one
    let mut farewell = "goodbye".to_owned();

    // Capture 2 variables: `greeting` by reference and
    // `farewell` by value.
    let diary = || {
        // `greeting` is by reference: requires `Fn`.
        println!("I said {}.", greeting);

        // Mutation forces `farewell` to be captured by
        // mutable reference. Now requires `FnMut`.
        farewell.push_str("!!!");
        println!("Then I screamed {}.", farewell);
        println!("Now I can sleep. zzzzz");

        // Manually calling drop forces `farewell` to
        // be captured by value. Now requires `FnOnce`.
        mem::drop(farewell);
    };

    // Call the function which applies the closure.
    apply(diary);

    // `double` satisfies `apply_to_3`'s trait bound
    let double = |x| 2 * x;

    println!("3 doubled: {}", apply_to_3(double));
}

Fn/FnMut/FnOnceの三種類のトレイトは、それぞれ以下のようになっていてFnMutはFnOnceをFnはFnMutをそれぞれ継承しています。それぞれの関数の引数を見ると、引数をどのように扱っているかが分かりますが一番過激なのがFnOnce、ただしdropしてしまうので一度しか使えない、変更だけならFnMut、参照するだけならFnというふうになります。

上記のコードでmem::drop(farewell);がなければ、F: FnOnce()はFnMutで問題無いし(ただしfn apply<f>(f: F) whereはfn apply<f>(mut f: F) whereに変更必要)farewell.push_str("!!!");がなければ(つまり引数を変更しなければ)Fnで問題ありません。

pub trait FnOnce
where
    Args: Tuple,
{
    type Output;

    // Required method
    extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
}

pub trait FnMut: FnOnce
where
    Args: Tuple,
{
    // Required method
    extern "rust-call" fn call_mut(
        &mut self,
        args: Args
    ) -> Self::Output;
}

pub trait Fn: FnMut
where
    Args: Tuple,
{
    // Required method
    extern "rust-call" fn call(&self, args: Args) -> Self::Output;
}

 

admin

associated function & method(@Rust)

https://doc.rust-jp.rs/rust-by-example-ja/fn/methods.html

のサンプルでsquareをtranslate()しただけで途中で結果の出力がないので補足、Pair部分はPoint ~~ Suareからは独立している部分になってます。

関連関数(associated function)はそのままの訳ですが、ここではPointという特定のタイプとだけ関連づけられているので、そのように呼ばれるようです。

一方メソッドは、構造体の文脈(ここでは構造体Rectangle)に関連づけられています。ここでrectangleとsquareという変数を作成していますが、交差する辺の長さが違うか同じかだけで、squareをmutableにすることでtranslate(ここでは平面移動)ができるようにしています。

// associated function & method
//
struct Point {
    x: f64,
    y: f64,
}

// Implementation block, all `Point` associated functions & methods go in here
impl Point {
    // This is an "associated function" because this function is associated with
    // a particular type, that is, Point.
    //
    // Associated functions don't need to be called with an instance.
    // These functions are generally used like constructors.
    fn origin() -> Point {
        Point { x: 0.0, y: 0.0 }
    }

    // Another associated function, taking two arguments:
    fn new(x: f64, y: f64) -> Point {
        Point { x: x, y: y }
    }
}

struct Rectangle {
    p1: Point,
    p2: Point,
}

impl Rectangle {
    // This is a method
    // `&self` is sugar for `self: &Self`, where `Self` is the type of the
    // caller object. In this case `Self` = `Rectangle`
    fn area(&self) -> f64 {
        // `self` gives access to the struct fields via the dot operator
        let Point { x: x1, y: y1 } = self.p1;
        let Point { x: x2, y: y2 } = self.p2;

        // `abs` is a `f64` method that returns the absolute value of the
        // caller
        ((x1 - x2) * (y1 - y2)).abs()
    }

    fn perimeter(&self) -> f64 {
        let Point { x: x1, y: y1 } = self.p1;
        let Point { x: x2, y: y2 } = self.p2;

        2.0 * ((x1 - x2).abs() + (y1 - y2).abs())
    }

    // This method requires the caller object to be mutable
    // `&mut self` desugars to `self: &mut Self`
    fn translate(&mut self, x: f64, y: f64) {
        self.p1.x += x;
        self.p2.x += x;

        self.p1.y += y;
        self.p2.y += y;
    }
}

// `Pair` owns resources: two heap allocated integers
struct Pair(Box, Box);

impl Pair {
    // This method "consumes" the resources of the caller object
    // `self` desugars to `self: Self`
    fn destroy(self) {
        // Destructure `self`
        let Pair(first, second) = self;

        println!("Destroying Pair({}, {})", first, second);
        // `first` and `second` go out of scope and get freed
    }
}

fn main() {
    let rectangle = Rectangle {
        // Associated functions are called using double colons
        p1: Point::origin(),
        p2: Point::new(3.0, 4.0),
    };

    // Methods are called using the dot operator
    // Note that the first argument `&self` is implicitly passed, i.e.
    // `rectangle.perimeter()` === `Rectangle::perimeter(&rectangle)`
    println!("Rectangle perimeter: {}", rectangle.perimeter());
    println!("Rectangle area: {}", rectangle.area());

    let mut square = Rectangle {
        p1: Point::origin(),
        p2: Point::new(2.0, 2.0),
    };

    // Error! `rectangle` is immutable, but this method requires a mutable
    // object
    //rectangle.translate(1.0, 0.0);
    // TODO ^ Try uncommenting this line

    // Okay! Mutable objects can call mutable methods
    square.translate(1.0, 1.0);
    println!("Square perimeter: {}", square.perimeter());
    println!("Square area: {}", square.area());


    //
    // separated from here
    //
    let pair = Pair(Box::new(1), Box::new(2));

    pair.destroy();

    // Error! Previous `destroy` call "consumed" `pair`
    //pair.destroy();
    // TODO ^ Try uncommenting this line
}

 

admin

 

 

std::iterの3種類の関数(@Rust)

https://doc.rust-jp.rs/rust-by-example-ja/flow_control/for.html

のサンプルに出てくるイテレータですが、iter(コレクションの要素を借用)、into_iter(コレクションからのデータの取り出し)、iter_mut(コレクションの要素をミュータブルで借用)の3種類があります。

crateのドキュメント、

https://doc.rust-lang.org/std/iter/

に記載がある通りなのですが、exampleから該当部分のコードを抜き出してみると、以下のようになります。


~~iter~~
    for name in names.iter() {
        match name {
            &"Ferris" => println!("There is a rustacean among us!"),

~~into_iter~~
    for name in names.into_iter() {
        match name {
            "Ferris" => println!("There is a rustacean among us!"),

~~iter_mut~~
    for name in names.iter_mut() {
        *name = match name {
            &mut "Ferris" => "There is a rustacean among us!",

iterとiter_mutは”借用”なので元のコレクション(name)は再利用できますが、into_iterは”取り出し”なので元のコレクションは使えなくなります。

 

admin

turbofish syntax(@Rust)

https://doc.rust-jp.rs/rust-by-example-ja/expression.html

にturbofish構文(syntax)というのが出てきて何なのかと思ったら、形(::())からきているらしい。なぜ::をつけないといけないかと言うと、::無しだと関数の呼び出しと区別できないから。

以下のコードはオリジナルから多少変えてあって、ブロックに書き直しています。この方が読みやすくなると感じるから。

fn main() {
    // string -> integer
    let sum = {
        let parsed: i32 = "5".parse().unwrap();
        let turbo_parsed = "10".parse::().unwrap();    // turbofish syntax

        parsed + turbo_parsed
    };
    println!("Sum: {:?}", sum);
}

 

admin