-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
復習 #4
Comments
chap4 所有権所有権のチェックはコンパイル時に行われ、実行時には実行速度に影響しない。 スタックとヒープスタック上のデータは全て「既知」の「固定サイズ」でなければならない。 ヒープにメモリを確保するとき、OSがヒープ上に十分な大きさの空き領域を見つけて、使用中にして、ポインタを返す。 所有権規則
String型文字列リテラルはプログラムにハードコードされる。 メモリと確保文字列リテラルはコンパイル時にサイズが判明しているため、ハードコードされる。 String型の場合
Rustは変数がスコープを抜ける時に、 drop を呼ぶ。 変数とデータの相互作用法: ムーブString型の構成要素
長さは文字列のバイト数、許容量はOSから受け取ったメモリ領域のバイト数 let s1 = String::from("hoge");
let s2 = s1; Copyトレイトはコピーにオーバーヘッドがかからないプリミティブ型に実装されていることが多い。 s1, s2 が同じメモリ領域を指している場合、スコープから抜けてdropが呼び出された時に二重解放のエラーになってしまう。 Rust では自動的にデータが deep copy されることはない。 変数とデータの相互作用法: クローンlet s1 = String::from("hoge");
let s2 = s1.clone(); とすると、ヒープのデータが実際にコピーされる。 let x1 = 1;
let x2 = x1; はx2の代入でコピーされる。 型自身やその一部が Drop トレイトを実装している場合、Copy トレイトを実装させてくれない。
所有権と関数関数に値を渡すことと、値を変数に代入することは似ている。 戻り値とスコープ値を返すことでも、所有権は移動する。 |
Chap4 所有権4.2 参照と借用let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("{} length = {}", s1, len);
fn calculate_length(s: &String) -> usize {
s.len()
} Stringはポインタ、サイズ、キャパシティを持っているため、s1 は左の3つを持っている。 参照は所有権を持っていないため、変数がスコープを抜けても drop されない。 可変な参照特定のスコープで、特定のデータに対しては、1つしか可変な参照を持てない。 競合状態
スコープが違えばOK let mut s = String::from("hello");
{
let r1 = &mut s;
} // r1はここでスコープを抜けるので、問題なく新しい参照を作ることができる
let r2 = &mut s; 不変参照があるスコープでは可変参照を持てない。 ダングリングポインタ他人に渡されてしまった可能性のあるメモリを指すポインタ。 要点
|
chap4.3 スライス型所有権のない別のデータ型 => スライス コレクション全体ではなく、そのうちの一連の要素を参照する。 文字列スライスlet s = String::from("hello world");
let hello = &s[0..5];
let world = &s[6..11]; スライスデータ構造
fn main() {
let mut s = String::from("hello world");
let word = first_word(&s); // word will get the value 5
// wordの中身は、値5になる
s.clear(); // this empties the String, making it equal to ""
// Stringを空にする。つまり、""と等しくする
// word still has the value 5 here, but there's no more string that
// we could meaningfully use the value 5 with. word is now totally invalid!
// wordはまだ値5を保持しているが、もうこの値を正しい意味で使用できる文字列は存在しない。
// wordは今や完全に無効なのだ!
println!("{}", s);
println!("{}", word);
}
fn first_word(s: &String) -> usize {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return i;
}
}
s.len()
}
fn first_word2(s: &String) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
} first_word2の時は、wordのライフタイムがsのライフタイムと一致しているので、println!("{}", word)がコンパイルエラーになる。 文字列リテラルはスライスlet s = "Hello, world!"; でsの型は &str &str を関数の引数にすると、&String, &str どちらでも渡すことができる。 |
https://chat.openai.com/c/eaa24dc4-9b8c-49d1-a946-5194a5f45b01 15. スマートポインタ参照はデータを借用するだけのポインタなのに対して、スマートポインタは指しているデータを所有している。
スマートポインタは Deref と Drop を実装している。 本章で取り扱うスマートポインタ
15.1 Boxスタック: ヒープへのポインタ 以下のような場面でよく使う
fn main() {
let b = Box::new(5);
println!("b = {}", b);
} 実際に確保されるのはヒープ上だが、スタックにあるのと同じように操作できる。 Boxにより再帰的な型定義ができるようになる再起的な型はコンパイル時に大きさがわからない。 非再帰的な型のサイズを計算する#![allow(unused)]
fn main() {
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
} variantの中で一番大きなサイズを必要とするものをMessage型のサイズとする。 |
15.2 DerefDeref トレイトを実装することで、参照外し演算子の * の振る舞いをカスタマイズできる。 参照外し演算子で値までポインタを追いかけるfn main() {
let x = 5;
let y = &x;
assert_eq!(5, x);
assert_eq!(5, *y);
} とすると、アサートがとおる assert_eq!(5, y); とすると、Integerと&{Integer} のPartialEqは実装されていないため、コンパイルエラーとなる。 Box を参照のように使うfn main() {
let x = 5;
let y = Box::new(x);
assert_eq!(5, x);
assert_eq!(5, *y);
} 参照外し演算子によってボックスのポインタを辿ることができる。 Deref トレイトを実装して型を参照のように扱うuse std::ops::Deref;
struct MyBox<T>(T);
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
} selfを借用し、内部のデータへの参照を返すメソッドを実装する。 *y
は
*(y.deref()) を内部的に行っている。 参照型外し強制コンパイラが関数、メソッドの実引数に行うもの。 fn hello(name: &str) {
println!("Hello, {}!", name);
}
fn main() {
let m = MyBox::new(String::from("Rust"));
hello(&m);
} 引数として &m を渡している。 m = Stringのポインタへのポインタ(スタック) -> Stringへのポインタ(ヒープ) -> Stringの実体(ヒープ) 参照外し型強制がない場合は以下のように書かなければいけない。 fn main() {
let m = MyBox::new(String::from("Rust"));
hello(&(*m)[..]);
} 引数の型に一致するまでDerefするが、Derefされる回数はコンパイル時に決定する。 参照外し型強制が可変性と相互作用する方法以下の場合に参照外し型強制する
struct BoxString(String);
impl Deref for BoxString {
type Target = String;
fn deref(&self) -> &String {
&self.0
}
}
impl DerefMut for BoxString {
fn deref_mut(&mut self) -> &mut String {
&mut self.0
}
}
fn main() {
let mut my_str = BoxString(String::from("Hello"));
// Derefを使った参照外し型強制
// &BoxStringから&Stringへの変換
println!("Length: {}", my_str.len());
// DerefMutを使った参照外し型強制
// &mut BoxStringから&mut Stringへの変換
my_str.push_str(", World!");
println!("{}", my_str);
}
fn make_uppercase(s: &mut BoxString) {
let uppercased = s.to_uppercase();
s.0 = uppercased;
}
fn main() {
let mut my_str = BoxString(String::from("Hello"));
// Convert the BoxString to uppercase
make_uppercase(&mut my_str);
// Now, we take a reference to my_str and get its length.
// Here, &mut BoxString is coerced to &String due to the Deref trait.
let length: usize = my_str.len();
println!("Uppercased: {}", my_str);
println!("Length: {}", length);
} |
https://chat.openai.com/c/711e99ac-22ef-4bb6-9747-3fca85e020f5 15.3 Dropスコープを抜けたときに起きることをカスタマイズできる。 struct CustomSmartPointer {
data: String,
}
impl Drop for CustomSmartPointer {
fn drop(&mut self) {
// CustomSmartPointerをデータ`{}`とともにドロップするよ
println!("Dropping CustomSmartPointer with data `{}`!", self.data);
}
} std::mem::drop を呼べば、スコープを抜けてDropが自動で呼ばれる前に手動で呼べる。 |
15.4 Rc複数の所有者を持つスマートポインタ。Referencing count の略。 enum List {
Cons(i32, Box<List>),
Nil,
}
use List::{Cons, Nil};
fn main() {
let a = Cons(5,
Box::new(Cons(10,
Box::new(Nil))));
let b = Cons(3, Box::new(a));
let c = Cons(4, Box::new(a));
} bを宣言するときにaの所有権が移る。その後、cを宣言するところでも所有権の移転の問題が発生してコンパイルエラーとなる。(value used after move) enum List {
Cons(i32, Rc<List>),
Nil,
}
use List::{Cons, Nil};
use std::rc::Rc;
fn main() {
let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
let b = Cons(3, Rc::clone(&a));
let c = Cons(4, Rc::clone(&a));
} Rc::cloneのたびに参照カウントが増える。 |
15.5 RefCellRc と異なり、RefCell 型は、保持するデータに対して単独の所有権を表す。 4章で学んだ借用規則
Boxは、借用規則の普遍条件はコンパイル時に強制される。 コンパイル時の精査では許容できない特定のメモリ安全な筋書きが許される。 内部可変性: 不変値への可変借用モックオブジェクトpub trait Messenger {
fn send(&self, msg: &str);
}
pub struct LimitTracker<'a, T: 'a + Messenger> {
messenger: &'a T,
value: usize,
max: usize,
}
impl<'a, T> LimitTracker<'a, T>
where T: Messenger {
pub fn new(messenger: &T, max: usize) -> LimitTracker<T> {
LimitTracker {
messenger,
value: 0,
max,
}
}
pub fn set_value(&mut self, value: usize) {
self.value = value;
let percentage_of_max = self.value as f64 / self.max as f64;
if percentage_of_max >= 0.75 && percentage_of_max < 0.9 {
// 警告: 割り当ての75%以上を使用してしまいました
self.messenger.send("Warning: You've used up over 75% of your quota!");
} else if percentage_of_max >= 0.9 && percentage_of_max < 1.0 {
// 切迫した警告: 割り当ての90%以上を使用してしまいました
self.messenger.send("Urgent warning: You've used up over 90% of your quota!");
} else if percentage_of_max >= 1.0 {
// エラー: 割り当てを超えています
self.messenger.send("Error: You are over your quota!");
}
}
} before #[cfg(test)]
mod tests {
use super::*;
struct MockMessenger {
sent_messages: Vec<String>,
}
impl MockMessenger {
fn new() -> MockMessenger {
MockMessenger { sent_messages: vec![] }
}
}
impl Messenger for MockMessenger {
fn send(&self, message: &str) {
self.sent_messages.push(String::from(message));
}
}
#[test]
fn it_sends_an_over_75_percent_warning_message() {
let mock_messenger = MockMessenger::new();
let mut limit_tracker = LimitTracker::new(&mock_messenger, 100);
limit_tracker.set_value(80);
assert_eq!(mock_messenger.sent_messages.len(), 1);
}
} after: MockMessanger: sent_messages を Vec => RefCell<Vec> にした。 #[cfg(test)]
mod tests {
use super::*;
use std::cell::RefCell;
struct MockMessenger {
sent_messages: RefCell<Vec<String>>,
}
impl MockMessenger {
fn new() -> MockMessenger {
MockMessenger { sent_messages: RefCell::new(vec![]) }
}
}
impl Messenger for MockMessenger {
fn send(&self, message: &str) {
self.sent_messages.borrow_mut().push(String::from(message));
}
}
#[test]
fn it_sends_an_over_75_percent_warning_message() {
// --snip--
assert_eq!(mock_messenger.sent_messages.borrow().len(), 1);
}
} RefCell で実行時に借用を追いかける不変参照、可変参照を作成するとき、それぞれ & と &mut 記法を使用する。 RefCell は現在活動中の Ref、RefMut の数を追いかける。 Rc と RefCellを組み合わせることで可変なデータに複数の所有者を持たせるRefCell を抱える Rcがあれば、複数の所有者を持ち、そして可変化できる値を得られる。 #[derive(Debug)]
enum List {
Cons(Rc<RefCell<i32>>, Rc<List>),
Nil,
}
use List::{Cons, Nil};
use std::rc::Rc;
use std::cell::RefCell;
fn main() {
let value = Rc::new(RefCell::new(5));
let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil)));
let b = Cons(Rc::new(RefCell::new(6)), Rc::clone(&a));
let c = Cons(Rc::new(RefCell::new(10)), Rc::clone(&a));
*value.borrow_mut() += 10;
match &b {
Cons(v1, list1) => match &**list1 {
Cons(v2, list2) => {
*v2.borrow_mut() += 10;
},
Nil => (),
}
Nil => (),
}
println!("a after = {:?}", a);
println!("b after = {:?}", b);
println!("c after = {:?}", c);
}
|
Rust 勉強会の開催に合わせて復習していく
https://iot-security-center.connpass.com/event/294154/
The text was updated successfully, but these errors were encountered: