CopyとClone(@Rust)

Rustの場合のCopyとCloneの例を見ると、

https://doc.rust-lang.org/std/marker/trait.Copy.html

のコードからlet y = xのように代入すると、

<これはmove>

#[derive(Debug)]
struct Foo;

let x = Foo;

let y = x;

// `x` has moved into `y`, and so cannot be used

// println!("{x:?}"); // error: use of moved value

したがって元のxにはアクセスできない。

 

<CopyをderiveすればCopy、ただしCloneもderiveしないとダメ>

“We can derive a `Copy` implementation. `Clone` is also required, as it’s

 a super trait of `Copy`.”

// We can derive a `Copy` implementation. `Clone` is also required, as it's
// a supertrait of `Copy`.
#[derive(Debug, Copy, Clone)]
struct Foo;

let x = Foo;

let y = x;

// `y` is a copy of `x`

println!("{x:?}"); // A-OK!

Cloneのドキュメントへのリンクは、

https://doc.rust-lang.org/std/clone/trait.Clone.html

さて、

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

のコードを再掲すると、

// A unit struct without resources
// Clone must also have to be derived when you use Copy
#[derive(Debug, Clone, Copy)]
struct Unit;

// A tuple struct with resources that implements the `Clone` trait
// Copy can not be applied to Pair struct
#[derive(Clone, Debug)]
struct Pair(Box, Box);

fn main() {
    // Instantiate `Unit`
    let unit = Unit;
    // Copy `Unit`, there are no resources to move
    let copied_unit = unit;

    // Both `Unit`s can be used independently
    println!("original: {:?}", unit);
    println!("copy: {:?}", copied_unit);


    // ----------- from here, Pair is handled -----------
    // Instantiate `Pair`
    let pair = Pair(Box::new(1), Box::new(2));
    println!("original: {:?}", pair);

    // Move `pair` into `moved_pair`, moves resources
    let moved_pair = pair;
    println!("moved: {:?}", moved_pair);

    // Error! `pair` has lost its resources
    //println!("original: {:?}", pair);
    // TODO ^ Try uncommenting this line

    // Clone `moved_pair` into `cloned_pair` (resources are included)
    let cloned_pair = moved_pair.clone();
    println!("cloned original: {:?}", moved_pair);
    // Drop the original pair using std::mem::drop
    drop(moved_pair);

    // Error! `moved_pair` has been dropped
    //println!("copy: {:?}", moved_pair);
    // TODO ^ Try uncommenting this line

    // The result from .clone() can still be used!
    println!("clone: {:?}", cloned_pair);
}

構造体の要素がプリミティブな型だとCopyが適用できるけれども、Box<i32>のような要素を持つとCopyは適用できない。

このソースを見ると、struct UnitにはCopyとCloneがderivesされていますがstruct Pair(Box, Box)にはCloneだけderiveコードを見るとCopyは暗黙(implicit)であり、Cloneはmoved_pair.clone()としているから明示的(explicit)であることがわかります。

 

admin

impl traitはstaticである(@Rust)

クロージャーは動的(コンパイル時点でサイズが決まっていない)なので、コンパイル可能とするためにはBox<dyn fn() >の様な形でヒープエリアに割り当てることが必要ですが、imple traitにするだけで静的(static)として扱える様になると。以下のreturns_closureとreturns_closure0関数は同じことをやっていますが、impl Trait(ここではFnトレイト)を使うと静的になるので見かけはスッキリしています。

// imple "Trait" is static, so you don't need to use "Box dyn"
fn main() {

    fn returns_closure(y: i32) -> impl Fn(i32) -> i32 {
        move |x| x + y
    }

    fn returns_closure0(y: i32) -> Box<dyn fn(i32) -> i32> {
        Box::new(move |x| x + y)
    }

    println!("{}", returns_closure0(50)(10));
    println!("{}", returns_closure(50)(20));
}

 

admin

 

 

iter()のfilter()内のクロージャー表記について(@Rust)

fn double_positives<'a>(numbers: &'a Vec) -> impl Iterator + 'a {
    numbers
        .iter()
        .filter(|x| x > &&0)
        .map(|x| x * 2)
}

fn main() {
    let singles = vec![-3, -2, 2, 3];
    let doubles = double_positives(&singles);
    assert_eq!(doubles.collect::<Vec<i32>>(), vec![4, 6]);
}

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

なぜここで二重参照記号なのか?

.filter(|x| x > &&0)

Geminiに聞くと、

———————————

iter().filter(|x| x > &&0) における && の必要性

上記のコードは、Rust でイテレータを用いて要素をフィルタリングする例です。 && は論理積演算子であり、このコードにおいて 2 つの重要な役割を果たします。

1. 参照の借用:

Rust では、所有権と借用の概念に基づいてメモリ管理が行われます。 iter() メソッドは、コレクションの要素への 参照 を返すイテレータを生成します。 この参照は、& 演算子によって示されます。

一方、クロージャ |x| x > &&0 は、イテレータから渡される各要素に対して評価されます。 このクロージャ内でも要素への参照にアクセスする必要があるため、& 演算子を用いて 2 重参照 &&x とする必要があります。

2. 短絡評価:

&& 演算子は 短絡評価 される性質を持ちます。 これは、左側のオペランドが false である場合、右側のオペランドが評価されずに false を返すことを意味します。

この例では、x > 0 は常に true を返します。 つまり、たとえ &&0 が存在しなくても、結果は常に true となります。 しかし、&& を省略すると、コンパイラから警告が出力されます。 これは、意図せずに 0 との比較が行われていないことを確認するためです。

まとめ:

  • && は、イテレータから渡される要素への 2 重参照 を表し、クロージャ内で要素にアクセスできるようにします。
  • && は 短絡評価 されるため、パフォーマンス面での影響はありません。
  • 冗長に見えますが、意図せぬ 0 との比較を防ぎ、コードの可読性を向上させる役割を果たします。

補足

  • クロージャ内の変数 x は、イテレータから渡される各要素への 不変参照 です。 つまり、クロージャ内で x を変更することはできません。
  • コードをより簡潔に記述したい場合は、filter(|&x| x > 0) のように && を省略することもできます。 ただし、コンパイラからの警告が出力されることに注意が必要です。

———————————

上記で、filter(|&x| x > 0)は嘘(&&は形式的には論理積演算子だけど実は二重参照だからこの説明も微妙な説明)で、以下のようにしないとコンパイルは通りません。生成A.I.はそれらしい回答は出してきますが、正しいかどうかは別なので、解決の手掛かりをうるために使うのが現段階では正解だろうと思う。

.filter(|&x| x > &0)

あるいは、

.filter(|x| *x > &0)

あるいは、

.filter(|x| **x > 0)

あるいは、

.filter(|&x| *x > 0)

もしくは、

.filter(|&&x| x > 0)

ということで比較対象とするには参照記号や参照外しで、比較の『次元』を合わせないといけないということ。

https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.filter

をみると、以下のような記述(クロージャーはfilter()に参照渡しだから二重参照になると)になってます

Because the closure passed to filter() takes a reference, and many iterators iterate over references, this leads to a possibly confusing situation, where the type of the closure is a double reference:

クロージャーに関するこの辺りの説明は、

https://zenn.dev/mebiusbox/books/22d4c1ed9b0003/viewer/64c6f3

「静的と動的に関わる記述」で詳しいと思う。

 

admin

協力乾燥剤の効果

3Dプリンタのフィラメント、吸湿防止でシリカゲル入りのケースに収納していますが、湿度45%ぐらいから下がらないので、強力乾燥剤投入。

およそ二十時間ぐらい経過で30%近くまで低下しています。これから梅雨時の必需品、

ただしシリカゲルと違って再生はできないようです。

 

admin

dyn prefix(@Rust)

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

におそらくdyn(dynamicの略)の典型的使い方(トレイトの戻り値型で必須のprefixキーワード)だろうと思われるコードが出てきます。

dynの説明、

https://doc.rust-lang.org/std/keyword.dyn.html

によると、dyn Traitは二つの参照ポインタを持つようで、

データへの参照(この場合にはstruct)ポインタ情報

② 通称vtableと呼ばれる関数ポインタマップへのポインタ情報

struct Sheep {}
struct Cow {}

trait Animal {
    // Instance method signature
    fn noise(&self) -> &'static str;
}

// Implement the `Animal` trait for `Sheep`.
impl Animal for Sheep {
    fn noise(&self) -> &'static str {
        "baaaaah!"
    }
}

// Implement the `Animal` trait for `Cow`.
impl Animal for Cow {
    fn noise(&self) -> &'static str {
        "moooooo!"
    }
}

// Returns some struct that implements Animal, but we don't know which one at compile time.
fn random_animal(random_number: f64) -> Box<dyn Animal> {
    if random_number < 0.5 {
        Box::new(Sheep {})
    } else {
        Box::new(Cow {})
    }
}

fn main() {
    let random_number = 0.234;
    let animal = random_animal(random_number);
    println!("You've randomly chosen an animal, and it says {}", animal.noise());
}

戻り値には選択的に二種類の構造体のヒープエリアBox::new(struct{})を指定していますが、コンパイル時には”animal”はそれぞれの構造体にリンクされるので、戻り情報のBox::new(struct{})には”animal”関数も含まれている(上記の①と②)から”animal.noise()“がアクセス可能となります。

もし戻り値の型が確定的であれば、例えばBox<Sheep>で問題ないのは蛇足で、確定できないからトレイトを戻り型にしている訳で、そのためのdynです。

動的なポインター割り当てなので、性能的には当然オーバーヘッドが出てきますが、コードの分かり易さの点からはこの記法で問題ないケースが大半だろうと思います。

 

admin

これはDefaultトレイトの標準実装では対応できない(@Rust)

 

https://doc.rust-jp.rs/rust-by-example-ja/scope/lifetime/trait.html

のライフタイムアサーションに出てくるコード、

// A struct with annotation of lifetimes.
// ライフタイムのアノテーションつき構造体。
#[derive(Debug)]
struct Borrowed<'a> {
    x: &'a i32,
}

// Annotate lifetimes to impl.
// ライフタイムのアノテーションつきimpl。
impl<'a> Default for Borrowed<'a> {
    fn default() -> Self {
        Self {
            x: &10,
        }
    }
}

fn main() {
    let b: Borrowed = Default::default();
    println!("b is {:?}", b);
}

ここでなぜ#[derive(Default)]ではダメなのか、それはまさしくBorrowed構造体のデータで参照を使っているので絶対にライフタイム指定が必要、かつライフタイムを指定した場合には構造体Borrowedに対してDefaultトレイトの標準実装では対応できないからカスタム実装が必要となります。

もしデータがmoveされる以下のコードならば、

// A struct with annotation of lifetimes.
// ライフタイムのアノテーションつき構造体。
#[derive(Debug, Default)]
struct Borrowed {
    x: i32,
}

// Annotate lifetimes to impl.
// ライフタイムのアノテーションつきimpl。
/*
impl<'a> Default for Borrowed<'a> {
    fn default() -> Self {
        Self {
            x: &10,
        }
    }
}
*/

fn main() {
    let b: Borrowed = Borrowed{x: 5,};
    println!("b is {:?}", b.x);
}

Defaultトレイトは標準実装で問題ありません

 

admin

Ubuntu 24.02で論理ボリュームの拡張

M1 MacBook AirにVMware Fusionインストしましたが、Ubuntuのインストールでデフォルトで割り当てるディスク容量は20GBで、さらに実際の論理ボリュームに割り当てられるのはその半分の10GB、デスクトップ環境にしただけで、すでに85%ぐらい消費しているのですぐに足りなくなるので拡張します。

Ubuntuの20.02からはLVMが標準になってるらしく、LVMは一言で言うと物理ボリュームと論理ボリュームの対応づけを柔軟に行える仮想化されたボリューム管理システムです。物理的に多くのサーバーでは複数のディスクが存在するだろうし、論理ボリュームという概念で複数の異なるディスクを同じ論理ボリュームにすることも当然必要になるから。

作業の参照は、

https://www.myit-service.com/blog/ubuntu-lvm/

<現状と関連するコマンド>

最初にディスクUTYでの見え方は、VMwareでディスク容量を30GBの物理ドライブにしただけで、そのうちの11GBだけがBlock Device(論理ボリューム)に割り当てられています

スクリーンショット 2024-05-17 13.28.22.png

 

・ディスク拡張に関連するコマンドと拡張前の状態

$ growpart

resize2fsと共にパーティションサイズの拡張に使われるらしけど、今回は使っていない

$ lsblk

list block devices、現在利用できるブロックデバイス(block device : Linuxにはブロックデバイスとキャラクターデバイスがありますが、一言で言えばファイルデバイス)の表示させる

スクリーンショット 2024-05-17 12.52.49.png

$ parted

パーティションの作成や削除などに使う、以下はparted起動してprint all実行の結果

スクリーンショット 2024-05-17 13.18.01.png

$ pvdisplay

物理ボリュームの詳細を表示する

スクリーンショット 2024-05-17 13.31.44.png

$ gparted

グラフィカルなインターフェースのpartedコマンド

スクリーンショット 2024-05-17 13.11.33.png

$ lvextend

LVM(RedHatではLVM2が標準らしいがUbuntuではLVMしかインストされていない)で論理ボリュームを拡張するコマンド

$ resize2fs

マウント状態で論理ボリューム変更を反映します

 

<拡張作業>

① 最初に仮想マシン(Ubuntu)の停止状態で、「仮想マシン」 →「設定」でディスク容量拡張しておく、今回は20 -> 30GBにしていますが元々が20GBのうちで論理ボリュームには10GBしか割り当てられていないから少なすぎ

② Ubuntu立ち上げて、GPartedで “->|”のアイコンで、対象のボリューム(/dev/nvme0n1p3)をリサイズします

③ $ lsblkでパーテションの割り付け確認して余裕容量を確認、残らず割り当てるにはlvextendコマンドで100%指定すればいいだけですが

④ $ sudo lvextend -L 27G /dev/ubuntu-vg/ubuntu-lv

で27.3GBの内27GBを割り当てします

⑤ $ sudo resize2fs /dev/ubuntu-vg/ubuntu-lv

変更反映のためにresize2fsを実行、マウント状態で実行できます(出来なかったら変えられない)

⑥ $ df

で確認、めでたくボリューム拡張が反映されました、

スクリーンショット 2024-05-17 13.44.21.png

 

admin

apple silicon版のVMwareにarm64版のUbuntu

VMwareも個人使用は無償のアナウンスがBroadcomからあったので、M1 MacBook AirにVMとUbuntuを入れてみた。これもIntel macからの移行の一つになるのかな

・VMwareのインスト

https://9to5mac.com/2024/05/14/vmware-fusion-pro-13-free-for-personal-use/

このリンクからアカウント取得してダウンロード、何回か弾かれてChromeで成功、多分混んでただけで、次の日の朝にやってみたらすんなりだったから

インストールでファイルが見つからないと言われる時の解決方法。

https://www.dev2qa.com/how-to-fix-file-not-found-error-when-launching-vmware-fusion-on-mac-os/

以前にVMwareインストールはしてないと思うけど、同じ対応(VMwareライブラリの削除)でインストできた。

・Ubuntuのインスト

Arm用のデスクトップ版は無いから、サーバー版をダウンロード、インストしてデスクトップ関連のソフトをアップデートする、このやり方だとあくまでOSの種類はサーバーに見えている

$ sudo apt install ubuntu-desktop

ただしこれは時間が相当に掛かる、一時間以上かかった

$ sudo reboot

で起動して、timezoneの変更

$ sudo timedatectl set-timezone Asia/Tokyo

これでとりあえずデスクトップ版のUbuntuは使えるようになったけど、macとのファイル共有にはVMware Toolsを入れないといけないのだけれども、方法が?

 

P.S. 2024.5.19

VMware toolsはなくとも、過去のUbuntuインスト時のやり方で共有できました。ディレクトリ名などは微妙に異なっていますが(Desktop -> デスクトップ)、

https://isehara-3lv.sakura.ne.jp/blog/2021/08/13/vmwareでゲストosとのフォルダ共有/

 

admin

Valgrind(メモリリークチェックツール)

Rustのドキュメントの中に出てきたキーワードですが、Valgrindなるツールがあるらしいので触ってみた。

M1 Macにはインストールできないので、久々にVMのUbuntuにインストールしてみた。

ついでにRustもインストール。

<Rust on Ubuntu>

参考サイト、

https://qiita.com/yoshiyasu1111/items/0c3d08658560d4b91431

$ curl –proto ‘=https’ –tlsv1.2 -sSf https://sh.rustup.rs | sh

ただしcurlがcurl: (23) Failure writing output to destinationのエラーを吐くので、

$ sudo snap remove curl

$ sudo apt install curl

でcurl再インストールしたらできました、この理屈は以下のようなものかと、

https://qiita.com/ynott/items/c7362ab3fdcee3fb0972

 

<Valgrindのインストール>

https://valgrind.org/info/

wikipediaによるとvalgrind自身はvmになっていて、バイナリを中間言語に変換して実行するらしいから、実行速度はかなり低下する(数分の1)けれども、メモリリークテストにとってはそれほど問題とならないという理屈のようです。バイナリを中間言語に変換するので、コンパイラが何であってもバイナリさえあれば実行可能です。

Linux(VM上のUbuntu)にソースからインストールしてみた、

————————————————

ダウンロードした最新のtar.bz2ファイルを

tar -jxvf valgrind-3.23.0.tar.bz2

展開されたソースフィイルに移動して、

$ ./configure

$ make 

$ sudo make install

インストールできたら動作確認、

main.rsというシンプルなRustのファイルを作って、

fn main() {

  println!("Hello, world!");

}

これをコンパイルしてmainファイル作成して以下を実行すると、

$ valgrind ls -l
==10251== Memcheck, a memory error detector
==10251== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al.
==10251== Using Valgrind-3.23.0 and LibVEX; rerun with -h for copyright info
==10251== Command: ls -l
==10251== 
合計 3700
-rwxrwxr-x 1 hoge hoge 3780776  5月 12 12:37 main
-rw-rw-r-- 1 hoge hoge      44  5月 12 12:36 main.rs
==10251== 
==10251== HEAP SUMMARY:
==10251==     in use at exit: 20,365 bytes in 9 blocks
==10251==   total heap usage: 140 allocs, 131 frees, 109,770 bytes allocated
==10251== 
==10251== LEAK SUMMARY:
==10251==    definitely lost: 0 bytes in 0 blocks
==10251==    indirectly lost: 0 bytes in 0 blocks
==10251==      possibly lost: 0 bytes in 0 blocks
==10251==    still reachable: 20,365 bytes in 9 blocks
==10251==         suppressed: 0 bytes in 0 blocks
==10251== Rerun with --leak-check=full to see details of leaked memory
==10251== 
==10251== For lists of detected and suppressed errors, rerun with: -s
==10251== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Rustなので問題は出ませんが、次にC++で意図的にリークが発生するような以下のソースでは、

#include <stdlib.h>
int main()
{
 char *x = new char[100];
 return 0;
}

以下のようにエラー(LEAK SUMMARY:)が発生します、この実行は実はintel MacOSにValgrindインストールして行ってますが、

% valgrind --tool=memcheck ./memory
==23180== Memcheck, a memory error detector
==23180== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==23180== Using Valgrind-3.23.0.GIT-lbmacos and LibVEX; rerun with -h for copyright info
==23180== Command: ./memory
==23180== 
--23180-- run: /usr/bin/dsymutil "./memory"
warning: no debug symbols in executable (-arch x86_64)
==23180== Syscall param map_with_linking_np(link_info) points to uninitialised byte(s)
==23180==    at 0x100067D2E: __map_with_linking_np (in /usr/lib/dyld)
==23180==    by 0x10001C88A: dyld4::setUpPageInLinkingRegions(dyld4::RuntimeState&, dyld4::Loader const*, unsigned long, unsigned short, unsigned short, bool, dyld3::Array const&, dyld3::Array const&) (in /usr/lib/dyld)
==23180==    by 0x10001C221: invocation function for block in dyld4::Loader::setUpPageInLinking(Diagnostics&, dyld4::RuntimeState&, unsigned long, unsigned long long, dyld3::Array const&) const (in /usr/lib/dyld)
==23180==    by 0x10001BF94: dyld4::Loader::setUpPageInLinking(Diagnostics&, dyld4::RuntimeState&, unsigned long, unsigned long long, dyld3::Array const&) const (in /usr/lib/dyld)
==23180==    by 0x10001CA08: dyld4::Loader::applyFixupsGeneric(Diagnostics&, dyld4::RuntimeState&, unsigned long long, dyld3::Array const&, dyld3::Array const&, bool, dyld3::Array const&) const (in /usr/lib/dyld)
==23180==    by 0x100022212: dyld4::JustInTimeLoader::applyFixups(Diagnostics&, dyld4::RuntimeState&, dyld4::DyldCacheDataConstLazyScopedWriter&, bool) const (in /usr/lib/dyld)
==23180==    by 0x100009DBC: dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*) (in /usr/lib/dyld)
==23180==    by 0x1000092FE: (below main) (in /usr/lib/dyld)
==23180==  Address 0x1048e7d00 is on thread 1's stack
==23180==  in frame #1, created by dyld4::setUpPageInLinkingRegions(dyld4::RuntimeState&, dyld4::Loader const*, unsigned long, unsigned short, unsigned short, bool, dyld3::Array const&, dyld3::Array const&) (???:)
==23180== 
==23180== 
==23180== HEAP SUMMARY:
==23180==     in use at exit: 9,441 bytes in 176 blocks
==23180==   total heap usage: 186 allocs, 10 frees, 10,173 bytes allocated
==23180== 
==23180== LEAK SUMMARY:
==23180==    definitely lost: 4,388 bytes in 135 blocks
==23180==    indirectly lost: 48 bytes in 1 blocks
==23180==      possibly lost: 600 bytes in 3 blocks
==23180==    still reachable: 4,405 bytes in 37 blocks
==23180==         suppressed: 0 bytes in 0 blocks
==23180== Rerun with --leak-check=full to see details of leaked memory
==23180== 
==23180== Use --track-origins=yes to see where uninitialised values come from
==23180== For lists of detected and suppressed errors, rerun with: -s
==23180== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 124 from 39)

Rustではあまり出番がなさそうですが、c++では使えるツールのようです。

 

admin

関連型(associated types)(@Rust)

ジェネリクスと関連し、ジェネリクスとの使い分けも必要ですが、関連型という記法があります。

https://zenn.dev/shinyay/articles/hello-rust-day040

のコードから、関連型で全体を構成してみました。

できるだけ固有の型記述を排除するために、impl Positionの中ではi32指定とするのではなく&Self::X or Yを使っています。

fn main() {
    // associated types
    //
    let point = Point(x, y);

    println!("Point X:{}, Y:{}", &x, &y);
    println!("Exist?:{}", point.exist(&x, &y));

    println!("Point-X:{}", point.v_axis());
    println!("Point-X:{}", point.h_axis());

    new_point(&point);
}

struct Point(i32, i32);

trait Position {
    type X;         // to use associated types
    type Y;

    fn exist(&self, _: &Self::X, _: &Self::Y) -> bool;
    fn h_axis(&self) -> i32;
    fn v_axis(&self) -> i32;
}

impl Position for Point {
    type X = i32;
    type Y = i32;

    fn exist(&self, x: &Self::X, y: &Self::Y) -> bool {
        (&self.0 == x) && (&self.1 == y)
    }

    fn h_axis(&self) -> Self::X {
        self.0
    }

    fn v_axis(&self) -> Self::Y {
        self.1
    }
}

fn new_point<Z: Position>(point: &Z) {                          // you don't need specify X and Y anymore
    println!("POINT:({},{})", point.v_axis(), point.h_axis())   // like "fn new_point<X, Y, Z>(point: &Z) where Z: Position<X, Y>"
}

使い分けですが、この例のようなシンプルなトレイトとSelfが一対一対応(トレイトに実装される型が一種類の時)の場合には、関連型を使う方がコードが読みやすいだろうと思います。もしi32以外に例えばf64の実装もあるというような複数実装の時には素直にジェネリクスを使うしかありません。

 

admin