Wikibooks
jawikibooks
https://ja.wikibooks.org/wiki/%E3%83%A1%E3%82%A4%E3%83%B3%E3%83%9A%E3%83%BC%E3%82%B8
MediaWiki 1.44.0-wmf.5
first-letter
メディア
特別
トーク
利用者
利用者・トーク
Wikibooks
Wikibooks・トーク
ファイル
ファイル・トーク
MediaWiki
MediaWiki・トーク
テンプレート
テンプレート・トーク
ヘルプ
ヘルプ・トーク
カテゴリ
カテゴリ・トーク
Transwiki
Transwiki‐ノート
TimedText
TimedText talk
モジュール
モジュール・トーク
熱力学/熱力学の第2法則
0
2039
264540
264324
2024-11-30T04:18:26Z
Nermer314
62933
264540
wikitext
text/x-wiki
{{pathnav|熱力学|frame=1}}
== 熱力学第二法則 ==
外部からエネルギーを受け取らずに、仕事を外部にし続けることができるような機関は、熱力学第一法則に抵触するから、実現不可能である。しかし、熱を外部から受け取り、これをすべて仕事に変換し、仕事によって発生した熱を熱源に返却する。これによって永久に動き続ける機関は、熱力学第一法則に矛盾しない。この永久機関を第二種永久機関と呼ぶ。第二種永久機関の実現は多くの科学者によって試みられたが、すべて失敗した。この経験的事実から、第二種永久機関は実現不可能であるということを熱力学の基本原理として採用する。
'''熱力学第二法則'''(ケルヴィンの原理あるいはオストヴァルトの原理)<ref>オストヴァルトの原理は「第二種永久機関は実現不可能である。」という原理である。ケルヴィンの原理は「一様な温度を持つ一つの熱源から正の熱を取り出し、これをすべて仕事に変換するだけで、他には何の変化も起こさないような過程は、実現不可能である」という原理である。</ref>
第二種永久機関、すなわち、一様な温度を持つ一つの熱源から正の熱を取り出し、これをすべて仕事に変換するだけで、他には何の変化も起こさないような過程は、実現不可能である。
単に、外部の熱源から受け取った熱をすべて仕事に変換する過程ならば、容易に実現可能である。例えば理想気体の等温膨張では、気体の内部エネルギーは変化しないから、外部からの熱をすべて仕事に変換する。
ケルヴィンの原理は次のクラウジウスの原理と等価である。
'''クラウジウスの原理'''
低温の熱源から、高温の熱源に正の熱を移し、他に何の変化を起こさないような過程は実現不可能である。
'''証明'''
背理法により証明する。まずは、ケルヴィンの原理が成り立たないならば、クラウジウスの原理が成り立たないことを証明する。ケルヴィンの原理が成り立たないならば、一様な温度を持つ一つの熱源から正の熱を取り出し、これをすべて仕事に変換するだけで、他には何の変化も起こさないような過程が実現可能である。この過程によって、低温の熱源から熱を取り出して仕事に変換できるから、取り出した仕事を用いて摩擦によって高温の熱源に熱を与えることができる。この過程は全体では、低温の熱源から取り出した熱を高温の熱源に移し、それ以外の変化は起こしていないから、クラウジウスの原理の反例になっている。
次に、クラウジウスの原理が成り立たないならば、ケルヴィンの原理が成り立たないことを証明する。まず、温度 <math>t_{\mathrm{H}} </math> の高温の熱源と、温度 <math>t_\mathrm{L} </math> の低温の熱源を使った、次の循環過程(カルノーサイクル)を考える。
# はじめ、シリンダー内の気体の温度は <math>t_ \mathrm{H} </math> である。シリンダーの体積は <math>V_1</math> である。
# シリンダーを高温の熱源 <math>t _\mathrm{H} </math> と接触させて、体積が <math>V_2</math> になるまで等温膨張する。このとき高温の熱源から <math>Q_{\mathrm H}</math> の熱を受け取る。
# シリンダーを熱源から絶縁して、シリンダー内の気体の温度が <math>t_\mathrm{L}</math> になるまで断熱膨張する。シリンダーの体積は <math>V_3</math> になる。
# シリンダーを低温の熱源 <math>t_\mathrm{L} </math> に接触させて、体積が <math>V_4</math> になるまで等温圧縮する。このとき、低温の熱源に <math>Q_\mathrm{L}</math> の熱を放出する。
# シリンダーを熱源から絶縁して、シリンダーの気体の温度が <math>t _\mathrm{H} </math> になるまで断熱圧縮する。シリンダーの体積は <math>V_1</math> になる。
このサイクルでは、サイクルを一周したときに内部エネルギーの変化はないから、 <math> W = Q_\mathrm{H} - Q_\mathrm{L} </math> の仕事を外部にする。
クラウジウスの原理が成り立たないならば、低温の熱源 <math>t_\mathrm{L} </math> から、高温の熱源 <math>t_\mathrm H </math> に正の熱 <math>Q_{\mathrm H}</math> を移し、他に何の変化を起こさないような過程が存在する。この過程によって、移動した熱 <math>Q_{\mathrm H}</math> をカルノーサイクルによって、高温の熱源から吸収し、これを仕事 <math>W</math> に変換し、低温の熱源に <math>Q_\mathrm{L}</math> の熱を放出することができる。全体では、低温の熱源から、<math> Q_\mathrm{H} - Q_\mathrm{L} </math> の熱を吸収し、これをすべて仕事に変換したことになる。これは、ケルヴィンの原理の反例である。
よって、ケルヴィンの原理とクラウジウスの原理が等価であることが証明できた。
=== 状態量 ===
気体の変数の変数p,V,Tは、理想気体であれ、ファンデルワールス気体であれ、状態方程式(理想気体かファンデルワールス気体かは、ここでは問わない)があるならば、変数p,V,Tのうちの、どれか二つが決まれば、気体の状態方程式から残りの変数も決まる。こうして3変数p,V,Tが決まる。
内部エネルギーは、理想気体であれ、ファンデルワールス気体であれ、どちらにしても、変数p,V,Tのうち、どれか二つが決まれば、気体の方程式から残りの方程式も決まる。決まった3変数のp,V,Tによって、内部エネルギーも決まってしまう。このような、状態変数によってのみ決まる物理量を'''状態量'''(じょうたいりょう)という。
3変数のp,V,Tが決まれば内部エネルギーも決定されるので、内部エネルギーは状態量である。
内部エネルギーを決める3変数のうち、真に独立変数なのは、そのうちの2個のみである。変数p,V,Tのどれを2個まで独立変数に選んでもいいが、残りの1個は既に選んだ変数の従属変数になる。
どの変数を独立変数に選ぶと、知りたい答えが求めやすいかは、問題による。
(多変数の関数の微分積分については、大学理科系で教育される。多変数関数の微分を偏微分という。解説は高校レベルを超えるので省略。)
=== 熱力学関数 ===
前節で言及された3つの変数(圧力p、体積V、温度T)のほか、エントロピーSや内部エネルギーUなども熱力学系の平衡状態を特徴付ける状態量である。
前節と同様、5つの状態量p,V,T,U,Sのうち任意の2つを独立変数に選ぶ場合にも、残る3つの変数はこれら2つの独立変数で表される従属変数として扱える。
この5つの変数の任意の組み合わせを独立変数にもつ状態量は、一般に熱力学関数と呼ばれる。
内部エネルギーU(S,V)のほか、後の章にて言及されるエントロピーS(U,V)、エンタルピーH(S,p)、ヘルムホルツの自由エネルギーF(V,T)、ギブスの自由エネルギーG(T,p)なども熱力学関数である。
=== 等温変化 ===
(この節では、高校数学の数学III相当の微分積分を用いる。分からなければ数学IIIを参照のこと。)
圧力をpと書くとする。体積をV、モル数をn、普遍気体定数をn、温度を絶対温度でTとする。
仕事Wの、瞬間的な仕事の大きさは微分を用いてdWと表せる。体積Vの、その瞬間の体積変化は微分を用いてdVと表せる。これらを用いれば、
<math> dW=pdV </math>
と微分方程式で表せる。(定圧変化では無いから、この式のpは変数である。)
体積をV<sub>1</sub>からV<sub>2</sub>まで変化させた時の仕事は、積分を用いて以下のように書き表せる。
<math> W=\int_{V_1}^{V_2} p dV </math>
これに、状態方程式の <math> pV = nRT </math> を、組み合わせる。
積分変数のVに合わせて、pを書き換えよう。
<math>p=\frac{nRT}{V}</math>
である。これより、仕事の式は、
<math> W=\int_{V_1}^{V_2} p dV= \int_{V_1}^{V_2} \frac{nRT}{V} dV = nRT\int_{V_1}^{V_2}\frac{dV}{V} = nRT\log \frac{V_2}{V_1 }</math>
となる。(なお、logは自然対数である。)
結論をまとめると、
:<math> W = nRT \log{\frac{V_2}{V_1}} </math>
である。
内部エネルギーUは、理想気体では温度のみの関数で、等温変化では温度が変化しないから、
:<math>\Delta U=0</math>
である。
したがって、等温変化では
:<math>Q=W</math>
である。
=== 断熱変化 ===
まず、熱と内部エネルギーと仕事の関係式
:<math>Q=U+W</math>
を、次のように微分方程式に書き換える。内部エネルギーの変化を微小変化としてdUと表したとすると、熱量Qや仕事Wも微小変化になるので、以下の様な式になる。
:<math>d'Q=dU+d'W</math>
QやWの微分演算記号dの上に点「<math>'</math>」が付いているのは、厳密に言うと、熱量Qや仕事Wは状態量で無いから、区別するために用いている。
断熱変化では
:<math>d'Q=0</math>
なので、つまり、
:<math>0=dU+d'W</math>
となる。
仕事に関しては
:<math>d'W=pdV</math>
である。
内部エネルギーの微小変化は、定積モル比熱を用いて、
:<math>dU=nC_{V}dT</math>
と書ける。
なので、これ等を式 <math>0=dU+d'W</math> に代入し、
:<math>0=nC_{V}dT+pdV</math>
と書ける。
両辺をpVで割ると、
:<math>0=\frac{nC_VdT}{pV}+{pdV}{pV}=\frac{nC_VdT}{pV}+\frac{dV}{V}</math>
であるが、pV=nRTを利用すると、
:<math>0=\frac{nC_VdT}{nRT}+\frac{dV}{V}=\frac{C_V}{R}\frac{dT}{T}+\frac{dV}{V}</math>
となる。
この微分方程式を解く。まず移項して、
:<math>\frac{dT}{T}=-\frac{R}{C_V}\frac{dV}{V}</math>
となる。
積分して、
:<math>\log T=- \frac{R}{C_V} \log{V}+Const</math>
ここで、<math>Const</math>は積分定数とする。(積分定数を <math>C</math> と書かなかったのは、比熱の記号との混同を避けるため。)
対数の性質より、係数R/Cvを対数log()の中の変数の指数に持ってこれる(数学II相当)ので、計算すると、
:<math>\log T=-\log{V^{\frac{R}{C_V}}}+Const</math>
さらに移項して、変数を左辺にまとめると、
:<math>\log T+\log V^{\frac{R}{C_V}}=Const</math>
対数の性質より、対数同士の和は、中の変数の積に変えられるので、
:<math>\log TV^{\frac{R}{C_V}}=Const</math>
である。
対数の定義より、自然対数の底をeとすれば
:<math>TV^{\frac{R}{C_V}}=e^{Const}</math>
である。
<math>e^{Const}</math>を新しく、別の定数として、定数“constant”と置き直せば、
:<math>TV^{\frac{R}{C_V}}=constant</math>
である。
これで断熱変化の温度と体積の関係式の公式が求まった。
;温度と体積の関係式
仕事Wとの関係を見たいので、先ほど求めた上の公式をpとTの式に書き換える事を考える。状態方程式<math>pV=nRT</math>を用いてTを、PとVを用いた式に書き換えると、まず代入しやすいように状態方程式を
:<math>T=\frac{pV}{nR}</math>
と書き換えて、これを公式に代入すれば、
:<math>TV^{\frac{R}{C_V}}=\frac{pV}{nR}V^{\frac{R}{C_V}}=\frac{1}{nR}pVV^{\frac{R}{C_V}}=\frac{1}{nR}pV^{1+\frac{R}{C_V}}=constant</math>
;圧力と体積の関係式
<math>\frac{1}{nR}</math>は定数なので、これを定数部にまとめてしまえば、別の定数をConst<sub>2</sub>とでも置いて、
:<math>pV^{1+\frac{R}{C_V}}=Const_2</math>
と書ける。
ここで、指数部の式は、マイヤーの式<math>Cp=Cv+R</math>より、定圧モル比熱で書き換えが可能である。
:<math>pV^{\frac{C_p}{C_V}}=Const_2</math>
である。
ここで、:<math>\frac{C_p}{C_V}</math>を{{ruby|'''比熱比'''|ひねつひ}}(heat capacity ratio)と言う。比熱比の記号は一般に<math>\gamma</math>で表す。
これを用いると、
:<math>pV^{\gamma}=Const_2</math>
である。
また、温度と体積の関係式
:<math>TV^{\frac{R}{C_V}}=constant</math>
に比熱比を代入すると、
:<math>TV^{\gamma -1}=constant</math>
になる。
これらの、圧力と体積の公式、および温度と体積の公式の二式を'''ポアソンの式'''という。
== 参考文献 ==
*エンリコ・フェルミ著、加藤正昭訳『フェルミ熱力学』三省堂、1973年。
[[Category:熱力学|ねつりきかくのたいにほうそく]]
2lss0my4rpk7nnu5qsp8ql3f30sv5o4
語学
0
2749
264550
251248
2024-11-30T10:14:41Z
Nermer314
62933
264550
wikitext
text/x-wiki
{{Navi| [[メインページ]] > [[人文科学]] > '''語学'''}}
{{NDC|810}}
{{NDC|820}}
{{NDC|830}}
{{NDC|840}}
{{NDC|850}}
{{NDC|860}}
{{NDC|870}}
{{NDC|880}}
{{NDC|890}}
{| style="float:right"
|-
|{{Wikipedia|語学|語学}}
|-
|{{Wikiquote|ことば|言語}}
|-
|{{Wiktionary|Category:言語|言語}}
|-
|{{Commons|Category:Language}}
|-
|{{wikiversity|School:語学と文学|語学}}
|-
|{{蔵書一覧}}
|-
|{{進捗状況}}
|}
語学に関する教科書が集められています。書き方等は[[Wikibooks:ウィキプロジェクト 語学|ウィキプロジェクト 語学]]を参照してください。
なお、語彙に関しては[[wikt:Wiktionary:メインページ|ウィクショナリー]]をご覧ください。
== 共通情報 ==
* [[新しい言語の習得法]]
* [[国際音声記号]]
== 日本語 ==
* [[日本語]]{{進捗|50%|2023-09-25}}
* [[小学校国語]]
* [[中学校国語]]
* [[高等学校国語]]
** [[高等学校国語総合|国語総合]]
** [[高等学校国語表現|国語表現]]
** [[高等学校現代文A|現代文A]]
** [[高等学校現代文B|現代文B]]
** [[高等学校古典A|古典A]]
** [[高等学校古典B|古典B]]
*[[日本語の方言|方言]](各方言についてはこのページを参照ください)
**[[北海道方言]]
***内陸部方言
***海岸部方言
**[[東北方言]]
***北奥羽方言
***南奥羽方言
**[[関東方言]]
***[[東関東方言]]
***[[西関東方言]]
***[[東京方言]]
***[[首都圏方言]]
**[[東海東山方言]]
***ナヤシ方言
***[[越後方言]]
***ギア方言
**[[八丈方言]]
**[[西日本方言]]
***[[近畿方言]]
***[[北陸方言]]
***[[中国方言]]
***[[雲白方言]]
***[[四国方言]]
**[[九州方言]]
***[[豊日方言]]
***[[肥筑方言]]
***[[薩隅方言]]
*[[琉球語]]
**[[奄美方言]]
**[[沖縄北部方言]]
**[[沖縄中南部方言]]
**[[宮古方言]]
**[[八重山方言]]
**[[与那国方言]]
*[[やさしい日本語]]
*[[日本語対応手話]]([[日本手話]]とは異なる)
== 孤立した言語 ==
(日本語を除く)
*[[アイヌ語]]
*[[ニヴフ語]]
*[[朝鮮語]](韓国語)
*[[ユカギール語]]
*[[ブルシャスキ語]]
*[[バスク語]]
*[[シュメール語]]
== シナ・チベット語族 ==
=== シナ語派 ===
*[[中国語]][[画像:25%.svg]]
**[[呉語]]
***[[上海語]]
***[[蘇州語]]
***[[温州語]]
**[[贛語]]
**[[閩語]]
***[[閩東語]]
****[[福州語]]
***[[台湾語]]、[[閩南語]]
****[[潮州語]]
****[[海南語]]
**[[客家語]]
**[[広東語]]
===チベット・ビルマ語派===
**[[チベット語]]
**[[レプチャ語]]
**[[ネワール語]]
**[[ビルマ語]]
**[[ペー語]]
**[[ゾンカ語]]
**[[メイテイ語]](マニプル語)
**[[ミゾ語]](ルシャイ語)
**[[コクバラ語]](コク・ボロック)
== タイ・カダイ語族 ==
*[[タイ語]]
*[[ラーオ語]]
*[[チワン語]]
== オーストロアジア語族 ==
*ベト・ムオン語派
**[[ベトナム語]]
*ムンダ語派
**[[サンタル語]]
**[[ムンダ語]]
*モン・クメール語派
**[[モン語]]
**[[クメール語]](カンボジア語)
**[[パラウン語]]
== オーストロネシア語族 ==
*台湾諸語
**[[タイヤル語]]
**[[アミ語]]
**[[パイワン語]]
**[[ブヌン語]]
**[[ツォウ語]]
*マレー・ポリネシア語派
**[[マレー語]]
**[[インドネシア語]]
**[[フィリピノ語]](タガログ語)
**[[セブアノ語]]
**[[マダガスカル語]](マラガシー語)
**[[ハワイ語]]
**[[マオリ語]]
**[[トンガ語]]
**[[ツバル語]]
**[[タヒチ語]]
**[[フィジー語]]
**[[サモア語]]
**[[パラオ語]]
**[[チャモロ語]]
**[[ナウル語]]
**[[キリバス語]]
== アルタイ諸語 ==
*モンゴル諸語
**[[モンゴル語]]
***[[キリル文字モンゴル語]]
***[[モンゴル文字モンゴル語]]
**[[ブリヤート語]]
**[[カルムイク語]]
*テュルク諸語
**南西語群
***[[トルコ語]]
***[[オスマン語]]
***[[アゼリー語]](アゼルバイジャン語)
***[[トルクメン語]]
**北西語群
***[[カザフ語]]
***[[キルギス語]]
***[[タタール語]]
**南東語群
***[[ウズベク語]]
***[[ウイグル語]]
***[[チャガタイ語]]
**北東語群
***[[トゥヴァ語]]
***[[ハカス語]]
**[[サハ語]](ヤクート語)
**[[チュワシ語]]
*ツングース諸語
**[[満州語]]
**[[シボ語]]
**[[エヴェンキ語]]
**[[ウイルタ語]]
== ウラル語族 ==
*フィン・ウゴル語派
**バルト・フィン諸語
***[[エストニア語]]
***[[フィンランド語]]
***[[リヴォニア語]]
***[[カレリア語]]
**ウゴル諸語
***[[ハンガリー語]]
**ボルガ・フィン諸語
*** [[マリ語]]
== チュクチ・カムチャツカ語族 ==
*[[チュクチ語]]
*[[コリャーク語]]
*[[イテリメン語]]
== エスキモー・アレウト語族 ==
*[[アレウト語]]
*[[ユピック語]]
*[[イニュピアック語]]
*[[イヌクティトゥット語]]
*[[グリーンランド語]]
== ドラヴィダ語族 ==
*南語派
**[[タミル語]]
**[[カンナダ語]]
**[[マラヤーラム語]]
*中南部語派
**[[テルグ語]]
== インド・ヨーロッパ語族 ==
=== インド・イラン語派 ===
*インド諸語
**[[ヒンディー語]]
**[[ウルドゥー語]]
**[[シンディー語]]
**[[グジャラート語]]
**[[シンハラ語]]
**[[コンカニ語]]
**[[マラーティー語]]
**[[ディベヒ語]]
**[[ネパール語]]
**[[パンジャブ語]]
**[[ベンガル語]]
**[[サンスクリット語]]
**[[パーリ語]]
**[[アッサム語]]
**[[オリヤー語]]
**[[カシミール語]]
*イラン諸語
**[[ペルシア語]]
**[[ダリー語]]
**[[タジク語]]
**[[クルド語]]
**[[パシュトゥン語]]
**[[バルチ語]]
**[[オセット語]]
**[[パミール諸語]]
**[[古代ペルシャ語]]
**[[アヴェスタ語]]
**[[パフラヴィー語]]
**[[ソグド語]]
=== ケルト語派 ===
*[[アイルランド語]]
*[[ウェールズ語]]
*[[ブルトン語]]
=== ゲルマン語派 ===
*西ゲルマン語群
** <span id="英語">[[英語]]</span>
***[[中学校英語]]
***高等学校英語
****[[高等学校英語文法]]
****[[高等学校英語リーディング]]
****[[高等学校英語ライティング]]
****[[高等学校英語オーラルコミュニケーション]]
***[[古英語]](古期英語)
***[[中英語]](中期英語)
**[[ドイツ語]]
***[[ドイツ語/表現集]]
**[[オランダ語]]
***[[アフリカーンス語]]
**[[フリジア語]]
*北ゲルマン語群
**[[アイスランド語]]
**[[ノルウェー語]]
**[[スウェーデン語]]
** [[デンマーク語]]
=== イタリック語派 ===
*[[ラテン語]]
*西ロマンス
**[[フランス語]]
**[[スペイン語]]
**[[ポルトガル語]]
**[[カタルーニャ語]]
*東ロマンス
**[[ルーマニア語]]
**[[イタリア語]]
=== スラヴ語派 ===
*東スラヴ語群
**[[ロシア語]]
**[[ウクライナ語]]
**[[ベラルーシ語]]
*西スラヴ語群
**[[ポーランド語]]
**[[カシューブ語]]
**[[チェコ語]]
**[[スロヴァキア語]]
*南スラヴ語群
**[[マケドニア語]]
**[[ブルガリア語]]
**[[スロヴェニア語]]
**[[セルビア語]]
**[[クロアチア語]]
=== バルト語派 ===
*[[リトアニア語]]
*[[ラトヴィア語]]
=== ギリシア語 ===
*[[ギリシア語]]
**[[古代ギリシア語]]
**[[古典ギリシア語]]
**[[新約ギリシア語]](コイネー)
**[[現代ギリシア語]]
=== 他の印欧語 ===
*[[アルバニア語]]
*[[アルメニア語]]
== アフロ・アジア語族 ==
*セム語派
**中央
***[[アラビア語]]
****[[正則アラビア語]](フスハー)
****[[アラビア語エジプト方言|エジプト方言]]
****[[アラビア語マグレブ方言|マグレブ方言]]
****[[マルタ語]]
***[[ヘブライ語]]
***[[アラム語]]
****[[シリア語]]
**南方
***西部
****[[アムハラ語]]
== ニジェール・コンゴ語族 ==
*[[ウォロフ語]]
*[[フラニ語]]
*[[バンバラ語]]
*[[マンディンカ語]]
*[[エウェ語]]
*[[スワヒリ語]]
*[[ズールー語]]
*[[ヨルバ語]]
*[[イボ語]]
== コーカサス諸語 ==
*南コーカサス語族
**[[グルジア語]]
**[[ミングレル語]]
*北西コーカサス語族
**[[アブハズ語]]
**[[ウビフ語]]
**[[アディゲ語]]
*北東コーカサス語族
**[[チェチェン語]]
**[[アヴァル語]]
**[[ダルギン語]]
**[[レズギ語]]
== 手話 ==
* [[日本手話]]
* [[イギリス手話]]
* [[アメリカ手話]]
* [[フランス手話]]
* [[ロシア手話]]
* [[中国手話]]
* [[ドイツ手話]]
* [[国際手話]]
== 計画言語・人工言語 ==
*[[エスペラント]]
*[[イド]]
*[[ヴォラピュク]]
*[[クリンゴン語]]
*[[ロジバン]]
*[[トキポナ]]
== 多言語の語学 ==
*[[トランスリンガル図鑑]]
== カテゴリツリー ==
{{#カテゴリツリー:語学}}
[[Category:語学|*]]
[[Category:書庫|こかく]]
euldu8kprht1y2j387z2k8vmz6pl7ud
民法第601条
0
4697
264531
228829
2024-11-30T02:16:58Z
Tomzo
248
/* 判例 */
264531
wikitext
text/x-wiki
[[法学]]>[[民事法]]>[[コンメンタール民法]]>[[第3編 債権 (コンメンタール民法)]]
{{wikipedia|賃貸借}}
==条文==
([[賃貸借]])
;第601条
:賃貸借は、当事者の一方がある物の使用及び収益を相手方にさせることを約し、相手方がこれに対してその賃料を支払うこと<u>及び引渡しを受けた物を契約が終了したときに返還すること</u>を約することによって、その効力を生ずる。
===改正経緯===
2017年改正にて、上記下線部を追加。
==解説==
==参照条文==
;第7節 賃貸借
:;第1款 総則
:*本条
:*[[民法第602条|第602条]]([[短期賃貸借]])
:*[[民法第603条|第603条]](短期賃貸借の更新)
::*[[民法第604条|第604条]](賃貸借の存続期間)
:;第2款 賃貸借の効力
:*[[民法第605条|第605条]](不動産賃貸借の対抗力)
:*[[民法第605条の2|第605条の2]](不動産の賃貸人たる地位の移転)
:*[[民法第605条の3|第605条の3]](合意による不動産の賃貸人たる地位の移転)
:*[[民法第605条の4|第605条の4]](不動産の賃借人による妨害の停止の請求等)
:*[[民法第606条|第606条]](賃貸人による修繕等)
:*[[民法第607条|第607条]](賃借人の意思に反する保存行為)
:*[[民法第607条の2|第607条の2]](賃借人による修繕)
:*[[民法第608条|第608条]](賃借人による費用の償還請求)
:*[[民法第609条|第609条]](減収による賃料の減額請求)
:*[[民法第610条|第610条]](減収による解除)
:*[[民法第611条|第611条]](賃借物の一部滅失等による賃料の減額等)
:*[[民法第612条|第612条]](賃借権の譲渡及び転貸の制限)
:*[[民法第613条|第613条]](転貸の効果)
:*[[民法第614条|第614条]](賃料の支払時期)
:*[[民法第615条|第615条]](賃借人の通知義務)
:*[[民法第616条|第616条]](賃借人による使用及び収益)
:*[[民法第616条の2|第616条の2]](賃借物の全部滅失等による賃貸借の終了)
:;第3款 賃貸借の終了
:*[[民法第617条|第617条]](期間の定めのない賃貸借の解約の申入れ)
:*[[民法第618条|第618条]](期間の定めのある賃貸借の解約をする権利の留保)
:*[[民法第619条|第619条]](賃貸借の更新の推定等)
:*[[民法第620条|第620条]](賃貸借の解除の効力)
:*[[民法第621条|第621条]](賃借人の原状回復義務)
:*[[民法第622条|第622条]](使用貸借の規定の準用)
:;第4款 敷金(第622条の2)
:*[[民法第622条の2|第622条の2]]
==判例==
#[http://www.courts.go.jp/search/jhsp0030?hanreiid=56138&hanreiKbn=02 建物収去土地明渡請求](最高裁判決 昭和28年12月18日)
#;対世的効力ある賃借権の妨害排除請求権
#:第三者に対抗できる借地権を有する者は、その土地に建物を建ててこれを使用する者に対し直接その建物の収去、土地の明渡を請求する妨害排除請求権を行使することができる。
#[http://www.courts.go.jp/search/jhsp0030?hanreiid=57299&hanreiKbn=02 借地権確認請求](最高裁判決 昭和30年04月05日)
#;対抗力のある賃借権と第三者に対する建物収去土地明渡請求の有無
#:第三者に対抗できる賃借権を有する者は、その土地に建物を有する第三者に対し、建物の収去、土地の明渡を請求することができる。
#[http://www.courts.go.jp/search/jhsp0030?hanreiid=65668&hanreiKbn=02 家屋明渡並びに損害賠償請求](最高裁判決 昭和35年06月23日)[[民法第613条]]
#;賃貸人の地位は賃貸物の譲渡に伴い当然に移動するか。
#:賃貸物の所有権が当初の賃貸人から順次移転し現在の現所有権者に帰した場合、当初の賃貸人と賃借人間の賃貸借は借家法上、現所有者に承継されたものと解すべきであるから現所有者は賃借人に本件家屋の使用収益をさせる義務を負う。
#:*本判例の趣旨は不動産に関しては2017年改正で新設された[[民法第605条の2|第605条の2]]に制定された。
#[https://www.courts.go.jp/app/hanrei_jp/detail2?id=52977 第三者異議等](最高裁判決 昭和36年12月21日)[[民法第612条]], [[借地借家法第34条]]
#;賃借人の債務不履行による賃貸借解除と転貸借の終了。
#:賃貸借の終了によつて転貸借は当然にその効力を失うものではないが、賃借人の債務不履行により賃貸借が解除された場合には、その結果転貸人としての義務に履行不能を生じ、よつて転貸借は右賃貸借の終了と同時に終了に帰する。
#[http://www.courts.go.jp/search/jhsp0030?hanreiid=53684&hanreiKbn=02 家屋明渡請求](最高裁判決 昭和37年12月25日)[[民法第896条]]
#;家屋賃借人の事実上の養子として待遇されていた者が賃借人の死後において家屋に居住できるとされた事例。
#:家屋賃借人の事実上の養子として待遇されていた者が賃借人の死後も引き続き家屋に居住する場合、賃借人の相続人らにおいて養子を遺産の事実上の承継者と認め、祖先の祭祀も同人に行わせる等(当審判決理由参照)の事情があるときは、その者は、家屋の居住につき、相続人らの賃借権を援用して賃貸人に対抗することができる。
#[http://www.courts.go.jp/search/jhsp0030?hanreiid=53732&hanreiKbn=02 建物退去土地明渡請求](最高裁判決 昭和38年02月21日)[[民法第454条]]
#;土地賃貸借の合意解除は地上建物の賃借人に対抗できるか。
#:土地賃借人と賃借人との間において土地賃貸借契約を合意解除しても、土地賃貸人は、特別の事情がないかぎり、その効果を地上建物の賃借人に対抗できない。
#[http://www.courts.go.jp/search/jhsp0030?hanreiid=54105&hanreiKbn=02 借地権確認等請求](最高裁判決 昭和43年10月08日)[[民法第163条]]
#;土地賃借権の時効取得
#:土地の継続的な用益という外形的事実が存在し、かつ、それが賃借の意思に基づくことが客観的に表現されているときは、土地賃借権を時効により取得することができる。
#[http://www.courts.go.jp/search/jhsp0030?hanreiid=53193&hanreiKbn=02 損害賠償請求](最高裁判決 昭和46年04月23日)[[民法第466条]]
#;賃貸土地の所有者がその所有権とともにする賃貸人たる地位の譲渡と賃借人の承諾の要否
#:賃貸借の目的となつている土地の所有者が、その所有権とともに賃貸人たる地位を他に譲渡する場合には、賃貸人の義務の移転を伴うからといつて、特段の事情のないかぎり、賃借人の承諾を必要としない。
#:*本判例の趣旨は不動産に関しては2017年改正で新設された[[民法第605条の2|第605条の2]]に制定された。
#[http://www.courts.go.jp/search/jhsp0030?hanreiid=52043&hanreiKbn=02 敷金返還請求](最高裁判決 昭和48年02月02日)
##'''敷金の被担保債権の範囲および敷金返還請求権の発生時期'''
##:家屋賃貸借における敷金は、賃貸借終了後家屋明渡義務履行までに生ずる賃料相当額の損害金債権その他賃貸借契約により賃貸人が賃借人に対して取得する一切の債権を担保するものであり、敷金返還請求権は、賃貸借終了後家屋明渡完了の時においてそれまでに生じた右被担保債権を控除しなお残額がある場合に、その残額につき具体的に発生するものと解すべきである。
##'''家屋の賃貸借終了後におけるその所有権の移転と敷金の承継の成否'''
##:家屋の賃貸借終了後明渡前にその所有権が他に移転された場合には、敷金に関する権利義務の関係は、旧所有者と新所有者との合意のみによつては、新所有者に承継されない。
##'''賃貸借終了後家屋明渡前における敷金返還請求権と転付命令'''
##:家屋の賃貸借終了後であつても、その明渡前においては、敷金返還請求権を転付命令の対象とすることはできない。
#[http://www.courts.go.jp/search/jhsp0030?hanreiid=52125&hanreiKbn=02 建物明渡請求](最高裁判決 昭和50年04月25日)[[民法第559条]]、[[民法第576条]]
#;賃借物につき第三者から明渡を求められた賃借人の賃料支払拒絶権
#:土地又は建物の賃借人は、賃借物に対する権利に基づき自己に対して明渡を請求することができる第三者からその明渡を求められた場合には、それ以後、賃料の支払を拒絶することができる。
#[http://www.courts.go.jp/search/jhsp0030?hanreiid=56322&hanreiKbn=02 賃借権設定仮登記抹消登記手続請求](最高裁判決 昭和52年02月17日) [[民法第369条]],[[民法第395条]]
#;競売手続が完結した場合と抵当権と同時に設定された抵当権者自身を権利者とする賃借権の帰すう
#:抵当不動産につき、抵当権者自身を権利者とする、賃借権又は抵当債務の不履行を停止条件とする条件付賃借権が設定され、その登記又は仮登記が抵当権設定登記と順位を前後して経由された場合において、競売申立までに対抗要件を具備した短期賃借権者が現われないまま、競落によつて第三者が当該不動産の所有権を取得したときには、特段の事情のない限り、抵当権者の賃借権は、それが短期賃借権であつても消滅する。
#[http://www.courts.go.jp/search/jhsp0030?hanreiid=52544&hanreiKbn=02 建物賃料等請求本訴、保証金返還請求反訴](最高裁判決 平成9年02月25日) [[民法第612条]]
#;賃借人の債務不履行による賃貸借の解除と賃貸人の承諾のある転貸借の帰すう
#:賃貸借が賃借人の債務不履行を理由とする解除により終了した場合、賃貸人の承諾のある転貸借は、原則として、賃貸人が転借人に対して目的物の返還を請求した時に、転貸人の転借人に対する債務の履行不能により終了する。
#[http://www.courts.go.jp/search/jhsp0030?hanreiid=62531&hanreiKbn=02 土地明渡請求事件](最高裁判決 平成16年07月13日)[[農地法第3条]],[[民法第163条]]
#;農地の賃借権の時効取得と農地法3条の適用の有無
#:時効による農地の賃借権の取得については,農地法3条の規定の適用はない。
#[http://www.courts.go.jp/search/jhsp0030?hanreiid=52401&hanreiKbn=02 預託金返還請求事件](最高裁判決 平成17年09月08日)[[民法第88条]]2項,[[民法第89条]]2項,[[民法第427条]],[[民法第896条]],[[民法第898条]],[[民法第899条]],[[民法第900条]],[[民法第907条]],[[民法第909条]]
#;共同相続に係る不動産から生ずる賃料債権の帰属と後にされた遺産分割の効力
#:相続開始から遺産分割までの間に共同相続に係る不動産から生ずる金銭債権たる賃料債権は,各共同相続人がその相続分に応じて分割単独債権として確定的に取得し,その帰属は,後にされた遺産分割の影響を受けない。
----
{{前後
|[[コンメンタール民法|民法]]
|[[第3編 債権 (コンメンタール民法)|第3編 債権]]<br>
[[第3編 債権 (コンメンタール民法)#2|第2章 契約]]<br>
[[第3編 債権 (コンメンタール民法)#2-7|第7節 賃貸借]]
|[[民法第600条]]<br>(損害賠償及び費用の償還の請求権についての期間の制限)
|[[民法第602条]]<br>(短期賃貸借)
}}
{{stub|law}}
[[category:民法|601]]
[[category:民法 2017年改正|601]]
ipxbpj6c9qk52o9v86ptn7axzwa4p1t
民事訴訟法第134条
0
8141
264534
247817
2024-11-30T02:38:36Z
Tomzo
248
/* 判例 */
264534
wikitext
text/x-wiki
[[法学]]>[[民事法]]>[[コンメンタール民事訴訟法]]
== 条文 ==
(訴え提起の方式)
;第134条
# 訴えの提起は、訴状を裁判所に提出してしなければならない。
# 訴状には、次に掲げる事項を記載しなければならない。
## 当事者及び法定代理人
## [[請求]]の趣旨及び原因
===改正経緯===
===2022年改正===
「住所、氏名等の秘匿制度」の創設に伴い、第133条に定められていた「訴え提起の方式」を本条に繰り下げ、第134条に定められていた「証書真否確認の訴え」を第134条の2とした。
===旧法===
*第223条〔訴え提起の方式〕
*:訴ノ提起ハ訴状ヲ裁判所ニ提出シテ之ヲ為スコトヲ要ス
*第224条〔訴状の記載事項〕
*#訴状ニハ当事者、法定代理人並請求ノ趣旨及原因ヲ記載スルコトヲ要ス
*#準備書面ニ関スル規定ハ訴状ニ之ヲ準用ス
== 解説 ==
{{wikipedia|訴状}}
訴えの提起は、原告が、被告との関係において、一定の権利の存否を主張し、当該権利主張の当否について、審理し判決することを裁判所に要求する行為である。
訴えが訴訟要件を具備していれば、裁判所は、当該請求の当否について審議し判決する事になるから、請求は、訴訟の対象であるということができ、その意味から訴訟物や訴訟対象と呼ばれる。
そして、原告の権利主張を理由なしとするときは、判決主文において「原告の請求を棄却する」と判示するのが実務慣行であり、逆に権利主張理由ありとして、原告の求めたとおりの内容の判決をするときは、講学上「原告の請求を認容する」というが、これらの場合も、請求の概念は判決の要求の意味に用いられている。
訴状には、本条2項に掲げる事項、すなわち、当事者、法定代理人、請求の趣旨及び原因を記載しなければならない。そして、事実についての主張を記載するには、できる限り、請求を理由付ける事実についての主張と、当該事実に関する事実についての主張とを区別して記載することが必要である([[民事訴訟規則第53条|規則第53条]]第2項)。すなわち訴状であるためには、これらを必ず明確に記載しなければならず、訴状に不備がみうけられ、裁判長が訴状の記載について必要な補正を促す場合には、補正命令を裁判所書記官に命じて行わせることができる([[民事訴訟規則第56条|規則第56条]])。この場合において、原告が注意すべきこととして、原告が不備を命じられたのにもかかわらず補正しないときは、裁判長は、命令で、訴状を却下しなければならない([[民事訴訟法第137条|第137条]]第2項)と規定されており、原告の請求は不適法として却下される。
なお、訴状には、原告が訴訟救助([[民事訴訟法第82条|第82条]])を許与された場合を除き、民事訴訟費用等に関する法律の定める訴額に応じた申立手数料を納付するため、手数料額だけ収入印紙を貼付し、作成者である原告又はその代理人において署名押印([[民事訴訟規則第2条|規則第2条]]第1項)することを必要とする。本人訴訟の場合において、1つの訴えで、複数の訴えがある場合において、訴額が不明な場合は、訴えを提起しようとしている管轄の裁判所書記官に問い合わせると教示してもらえる。
===訴訟物理論===
{{wikipedia|訴訟物}}
;訴訟物理論が機能するとされる局面
#既判力の客観的範囲
#*[[民事訴訟法第114条|第114条]](既判力の範囲)第1項
#重複訴訟の禁止
#*[[民事訴訟法第142条|第142条]](重複する訴えの提起の禁止)
#訴えの変更
#*[[民事訴訟法第143条|第143条]](訴えの変更)
#訴えの併合
#*[[民事訴訟法第136条|第136条]](請求の併合)
== 参照条文 ==
== 判例 ==
# [https://www.courts.go.jp/app/hanrei_jp/detail2?id=57514 慰籍料並に名誉回復請求](最高裁判決 昭和31年7月20日)[[民法第44条|民法44条]](現・[[一般社団法人及び一般財団法人に関する法律第78条]]),[[民法第709条|民法709条]],[[民法第723条|民法723条]]
#;法人に対する民法第44条に基く請求と同法第715条に基く請求との訴訟物の異同
#:法人に対する民法第44条に基く損害賠償の請求と同法第715条に基く損害賠償の請求とは、訴訟物を異にする。
#:*民法44条による法人の責任と同715条による法人の責任とは、発生要件を異にし法律上別個のものと解すべき。
#[https://www.courts.go.jp/app/hanrei_jp/detail2?id=84488 建物賃料増額確認請求事件](最高裁判決 平成26年9月25日)[[借地借家法第32条]],[[民事訴訟法第114条|民訴法114条]]1項
#;借地借家法32条1項の規定に基づく賃料増減請求により増減された賃料額の確認を求める訴訟の確定判決の既判力
#:借地借家法32条1項の規定に基づく賃料増減請求により増減された賃料額の確認を求める訴訟の確定判決の既判力は,原告が特定の期間の賃料額について確認を求めていると認められる特段の事情のない限り,前提である賃料増減請求の効果が生じた時点の賃料額に係る判断について生ずる。
----
{{前後
|[[コンメンタール民事訴訟法|民事訴訟法]]
|[[コンメンタール民事訴訟法#2|第2編 第一審の訴訟手続]]<br>
[[コンメンタール民事訴訟法#1-6|第1章 訴え]]
|[[民事訴訟法第133条の4|第133条の4]]<br>(秘匿決定の取消し等)
|[[民事訴訟法第134条の2|第134条の2]]<br>(証書真否確認の訴え)
}}
{{stub|law}}
[[category:民事訴訟法|134]]
[[category:民事訴訟法 2022年改正|134]]
69loohriqfgi54xpn4lgqcagrktsq2k
民事訴訟法第114条
0
8257
264533
260271
2024-11-30T02:37:46Z
Tomzo
248
/* 判例 */
264533
wikitext
text/x-wiki
[[法学]]>[[民事法]]>[[コンメンタール民事訴訟法]]
==条文==
([[既判力]]の範囲)
;第114条
# 確定判決は、主文に包含するものに限り、既判力を有する。
# 相殺のために主張した請求の成立又は不成立の判断は、相殺をもって対抗した額について既判力を有する。
==解説==
{{wikipedia|既判力}}
既判力の客観的範囲といわれる。なお、主観的範囲に関しては[[民事訴訟法第115条|次条]]にて定める。
1項の「主文に包含するもの」とは、訴訟物たる権利をいい、理由中の判断はこれに含まれない。訴訟物の単複異同は、判例実務のとる旧訴訟物理論に従えば、実体法上の権利の単複異同に従って決定される。
「主文に包含するもの」とは「判決主文」のことではない点は注意を要する。例えば判決主文中、引換給付を命じる部分は、執行開始要件であって、訴訟物ではないので、その部分の判断は既判力を生じないと考えられている。
==参照条文==
==判例==
#[http://www.courts.go.jp/search/jhsp0030?hanreiid=57462&hanreiKbn=02 損害賠償請求] (最高裁判決 昭和32年01月31日)[[民法第709条]],民訴法199条1項(現・本条),民訴法709条(→民事執行法)
#;後訴における主張が前訴の既判力に牴触しない一事例
#:甲が乙を相手として船舶の所有権確認、同引渡請求の訴(前訴)につき勝訴の確定判決を得た上、さらに乙を相手として、右船舶の滅失毀損による不法行為を理由とし損害賠償請求の訴(後訴)を提起した場合、たとえ前訴において、乙が右船舶を現に占有している事実を認めていたとしても、後訴において、乙が、右船舶は前訴の口頭弁論終結前乙において売却処分し、その頃右船舶が滅失毀損したと主張することは、なんら前訴の既判力に牴触しない。
#[https://www.courts.go.jp/app/hanrei_jp/detail2?id=52994 不動産所有権移転登記手続等請求](最高裁判決 昭和36年12月12日)[[民法第550条]]
#;書面によらない贈与を認める判決が確定した後の民法第550条による右贈与の取消の可否。
#:書面によらない贈与による権利の移転を認める判決が確定した後は、既判力の効果として民法第550条による取消権を行使して右贈与による権利の存否を争うことは許されない。
#[http://www.courts.go.jp/search/jhsp0030?hanreiid=36605&hanreiKbn=02 損害賠償請求事件](最高裁判決 平成20年07月10日)
#;前訴において1個の債権の一部についてのみ判決を求める旨が明示されていたとして,前訴の確定判決の既判力が当該債権の他の部分を請求する後訴に及ばないとされた事例
#:Xが,Yに対し,県が買収を予定していた土地上の樹木についてYがした仮差押命令の申立ての違法を理由として,本案訴訟の応訴等に要した弁護士費用相当額の賠償を求める前訴を提起した後に,同一の不法行為に基づき,県からの買収金の支払が遅れたことによる損害の賠償を求める後訴を提起した場合において,Xは,前訴において,上記仮差押命令の申立てがXによる上記土地の利用と買収金の受領を妨害する不法行為であるとして,買収金の受領が妨害されることによる損害が発生していることをも主張していたものということができるなど判示の事情の下では,Xが前訴において請求する損害賠償請求権と後訴において請求する損害賠償請求権とは1個の債権の一部を構成するものではあるが,前訴において1個の債権の一部についてのみ判決を求める旨が明示されていたものと解すべきであり,前訴の確定判決の既判力は後訴に及ばない。
#[https://www.courts.go.jp/app/hanrei_jp/detail2?id=84488 建物賃料増額確認請求事件](最高裁判決 平成26年9月25日)[[借地借家法第32条]],[[民事訴訟法第134条|民訴法134条]]
#;借地借家法32条1項の規定に基づく賃料増減請求により増減された賃料額の確認を求める訴訟の確定判決の既判力
#:借地借家法32条1項の規定に基づく賃料増減請求により増減された賃料額の確認を求める訴訟の確定判決の既判力は,原告が特定の期間の賃料額について確認を求めていると認められる特段の事情のない限り,前提である賃料増減請求の効果が生じた時点の賃料額に係る判断について生ずる。
----
{{前後
|[[コンメンタール民事訴訟法|民事訴訟法]]
|[[コンメンタール民事訴訟法#1|第1編 総則]]<br>
[[コンメンタール民事訴訟法#1-5|第5章 訴訟手続]]<br>
[[コンメンタール民事訴訟法#1-5-5|第5節 裁判]]
|[[民事訴訟法第113条|第113条]]<br>(公示送達による意思表示の到達)
|[[民事訴訟法第115条|第115条]]<br>(確定判決等の効力が及ぶ者の範囲)
}}
{{stub|law}}
[[category:民事訴訟法|114]]
8qoudlsqmxvh63yi5l87oqnxm09hvdk
借地借家法第34条
0
9263
264532
99277
2024-11-30T02:17:10Z
Tomzo
248
264532
wikitext
text/x-wiki
[[法学]]>[[民事法]]>[[借地借家法]]>[[コンメンタール借地借家法]]
==条文==
(建物賃貸借終了の場合における転借人の保護)
;第34条
#建物の転貸借がされている場合において、建物の賃貸借が期間の満了又は解約の申入れによって終了するときは、建物の賃貸人は、建物の転借人にその旨の通知をしなければ、その終了を建物の転借人に対抗することができない。
#建物の賃貸人が前項の通知をしたときは、建物の転貸借は、その通知がされた日から6月を経過することによって終了する。
==解説==
適法な転借人を保護するため、賃貸人に通知義務を定めた。
==参照条文==
==判例==
#[https://www.courts.go.jp/app/hanrei_jp/detail2?id=52977 第三者異議等](最高裁判決 昭和36年12月21日)[[民法第601条]],[[民法第612条]]
#;賃借人の債務不履行による賃貸借解除と転貸借の終了。
#:賃貸借の終了によつて転貸借は当然にその効力を失うものではないが、賃借人の債務不履行により賃貸借が解除された場合には、その結果転貸人としての義務に履行不能を生じ、よつて転貸借は右賃貸借の終了と同時に終了に帰する。
#[https://www.courts.go.jp/app/hanrei_jp/detail2?id=52262 建物明渡等請求事件] (最高裁判決 平成14年03月28日)[[民法第1条]]2項,[[民法第612条]]
#;事業用ビルの賃貸借契約が賃借人の更新拒絶により終了しても賃貸人が信義則上その終了を再転借人に対抗することができないとされた事例
#:ビルの賃貸,管理を業とする会社を賃借人とする事業用ビル1棟の賃貸借契約が賃借人の更新拒絶により終了した場合において,賃貸人が,賃借人にその知識,経験等を活用してビルを第三者に転貸し収益を上げさせることによって,自ら各室を個別に賃貸することに伴う煩わしさを免れるとともに,賃借人から安定的に賃料収入を得ることを目的として賃貸借契約を締結し,賃借人が第三者に転貸することを賃貸借契約締結の当初から承諾していたものであること,当該ビルの貸室の転借人及び再転借人が,上記のような目的の下に賃貸借契約が締結され転貸及び再転貸の承諾がされることを前提として,転貸借契約及び再転貸借契約を締結し,再転借人が現にその貸室を占有していることなど判示の事実関係があるときは,賃貸人は,信義則上,賃貸借契約の終了をもって再転借人に対抗することができない。
----
{{前後
|[[コンメンタール借地借家法|借地借家法]]
|[[コンメンタール借地借家法#3|第3章 借家]]<br>
[[コンメンタール借地借家法#3-2|第2節 建物賃貸借の効力]]<br>
|[[借地借家法第33条]]<br>(造作買取請求権)
|[[借地借家法第35条]]<br>(借地上の建物の賃借人の保護)
}}
{{stub|law}}
[[category:借地借家法|34]]
jl261wsdwgzco0ze4xdb1gduytzunc6
借地借家法
0
10706
264538
132418
2024-11-30T02:54:12Z
Tomzo
248
264538
wikitext
text/x-wiki
ここでは、借地借家法について扱います。
== 対象 ==
借地借家法は、平成3年10月4日に成立し、平成4年8月1日から施行されている法律であり、それまでの土地や建物の賃借人の保護のための法律である、建物保護ニ関スル法律、借地法、借家法の内容を統合し、これらに代わるものとして制定されました。
借地借家法は借地と建物賃貸借を対象とした民法の特別法です。借地とは、建物の所有を目的とする地上権または土地の賃借権([[借地借家法第2条]]1号)であり、借地借家法では、地上権と賃借権とを同様に扱っています。借地といえるためには建物の所有を目的とするものであることが必要であり、例えば駐車場としての利用を目的とした土地の賃借権は借地権ではなく、借地借家法の適用はありません。どのような場合に建物所有目的といえるかについて、所有を目的とするとは、借地人の借地しようの主たる目的がその地上に建物を築造し、これを所有することにある場合を言うものとされており(最判昭和42年12月5日民集21巻10号2545頁)、契約内容や実際の使用形態、利用目的などによって判断されることとなります。この昭和42年判例では、ゴルフ練習場の事務所用の建物について、土地をゴルフ練習場に利用するための従たる目的に過ぎないから建物所有目的でないされています。一方、自動車教習所の経営のためには校舎や事務室などの建物が不可欠であるとして、建物所有の目的が肯定された判例(最判昭和58年9月9日判時1092号59頁)もあります。
また建物については、借家との名称となっていますが全ての建物賃貸借が借地借家法の適用対象とされており、それが居住用のものであるか事業用のものであるかを問わず、また居住と関係のない、店舗や事務所、工場、倉庫などの建物賃貸借についても借地借家法による規制の対象となります。なおあくまで賃貸借であり、使用貸借は借地借家法の対象とはなりません。
== 借地 ==
=== 対抗力 ===
[[債権各論/賃貸借|賃貸借]]の頁で扱ったように、民法の規定によれば、賃借権はその登記を備えることで第三者への対抗力を持ちます。しかし賃借権の登記がなされるのは稀であり、借地借家法は賃借権が対抗力を備える場合を拡張しています。
借地については、借地権の登記がなされていなくとも、借地上の建物の登記があれば、借地権者は借地の譲受人などの第三者に対し、借地権を対抗することができます([[借地借家法第10条]]1項)。
そこで、原則として、まず借地上に建物が存在していることが必要となります。ただし一旦建築された建物が滅失した場合については例外規定がおかれています([[借地借家法第10条]]2項)。借地が二筆以上に分かれている場合、登記された建物が存在する土地についてのみ借地権を対抗できるというのが判例(最判昭和40年6月29日民集19巻4号1027頁など)です。また、建物所有者と借地権者とが同一であることも必要です。借地権者が借地上に登記された建物を所有しておれば、当該土地の所有権に利害関係を持つ者は、建物の登記名義によってその名義人が建物の所有することが出来る借地権を有することを推知でき、借地利用権限それ自体の登記なしに対抗力を認めても第三者に不測の損害を被らせる虞はないと考えられています。
建物について借地権者と異なる家族名義の登記がなされる場合や、譲渡担保が設定され譲渡担保権者に所有権移転登記をしたような場合がありますが、借地権がその登記なくして対抗力を有するためには、建物所有者と建物名義人が一致していることも必要とされています(最大判昭和41年4月27日民集20巻4号870頁(同居同姓の長男名義)、最判昭和47年6月22日民集26巻5号1051頁(妻名義)、最判平成元年2月7日判時1319号102頁(担保権者名義)において対抗力を否定)。他人名義の建物登記であれば、自己の建物所有権すら第三者に対抗できないのであるから、まして借地権については第三者に対抗できないと考えられたのです。もっとも学説では、同居の親族名義であるような場合については対抗力を認めるのが妥当との見解や、より一般的に異なる名義であってもそこに借地権が存在することはわかるとして対抗力を認める見解も有力です。
なお、建物登記がなく第三者に対抗できない場合であっても、そのことを奇貨として借地権設定者が実質的に同一人と見ることができるような者に土地所有権を譲渡し、土地の明け渡しを求めた場合などにおいては、権利濫用としてそれが否定されることがあります(最判昭和38年5月24日民集17巻5号639頁)。
=== 存続期間と更新 ===
借地権の存続期間については、最短で30年と定められています([[借地借家法第3条|借地借家法第3条]])。そこで30年未満の期間を定めた場合には期間は30年となり、30年以上の期間を定めた場合にのみその合意した期間となります。最初の更新後の期間は最短20年であり、その後の更新後の期間は最短10年となります([[借地借家法第4条|借地借家法第4条]])。
また、一定の場合には契約が更新されたものとみなす制度を採用しており、これを法定更新と言います(なお民法は更新の「推定」です)。法定更新は、借地借家法5条において定められており、1項は、「借地権の存続期間が満了する場合において、借地権者が契約の更新を請求したときは、建物がある場合に限り、前条の規定によるもののほか、従前の契約と同一の条件で契約を更新したものとみなす。ただし、借地権設定者が遅滞なく異議を述べたときは、この限りでない。」と定め、また2項は、「借地権の存続期間が満了した後、借地権者が土地の使用を継続するときも、建物がある場合に限り、前項と同様とする。」と定めています。
そこで、存続期間満了にあたり借地権者から更新請求がなされ、その際借地上に建物がある場合や、借地権が存続期間満了後も借地権者がその使用を継続し、建物も存続している場合には原則として更新がされ、ただし、借地権設定者が遅滞なく異議を述べたときには、更新がなされないこととなります。そして、借地権設定者が異議を述べて更新を拒絶するためには、正当事由が必要です([[借地借家法第6条]])。借地借家法6条では、正当事由の有無の判断要素として以下のものを挙げています。
# 借地権設定者および借地権者が土地の使用を必要とする事情
# 借地に関する従前の経過。例えば借地期間の長さや債務不履行の有無、程度、借地権設定者の態度など。
# 土地の利用状況。例えば借地上の建物の規模や構造、借地の利用形態など。
# 借地権設定者が土地の明渡しの条件として、あるいは土地の明渡しの引き換えに財産上の給付をする旨の申出をした場合のその申出(立退料)。
そして、このような正当事由の有無は借地権設定者が異議を申し立てたときを基準として判断されます(最判平成6年10月25日民集48巻7号1303頁)。また、4.の立退料の提供はあくまで正当事由を補完するものに過ぎず、他に正当の事由となる事実が存在することを前提としたものであり、多額の立退料の支払いを申し出たとしても、他の正当事由の内容となる事実がない場合には、更新拒絶は認められません。
また、立退料については、裁判所は異議を述べた後に申し出られた金額を考慮に入れて、異議を述べた時点での当該異議についての正当事由を判断することができるものとされており(最判平成3年3月22日民集45巻3号293頁)、異議を述べた後の立退料の申し出やその増額も考慮されます。なお、裁判所は借地権設定者の申し出た額を越える相当な立退料の支払いを条件として、借地権者に明け渡しを命じることもできます(最判昭和46年11月25日民集25巻8号1343頁)。
ただし、臨時設備の設置その他一時使用のために借地権を設定したことが明らかな場合には、借地借家法3条は適用されません([[借地借家法第25条]])。そこで、一時使用目的と認められるためには、単に短期の期間が合意されたというだけでは足りないものと考えられます。判例(最判昭和45年7月21日民集24巻7号1091頁)では、その対象とされた土地の利用目的、地上建物の種類、設備、構造、賃貸期間等諸般の事情を考慮し、賃貸借当事者間に、短期間にかぎり賃貸借を存続させる合意が成立したと認められる客観的合理的理由が存することを要するものとしています。
=== 建物買取請求権 ===
更新がなされなかった場合、借地権者は、借地権設定者に対して地上建物および借地権者が権原によって土地に付属させた物の時価での買取を請求することができます([[借地借家法第13条|借地借家法第13条]]1項)。これは形成権であり、借地権者の一方的意思表示により、売買契約が成立したのと同様の法律効果が生じることになります(大判昭和11年5月26日民集15巻998頁、最判昭和42年9月14日民集21巻7号1791頁など)。これは、建物が取り壊されることによる社会的損失を防ぎ、借地権者に建物の残存価値を取得する機会を与えるため定められたものです。売買契約が成立したこととなる結果、建物の明渡しと建物代金の支払いは同時履行の関係となり、またその敷地についても留置権が認められます。そこで、借地権者は建物代金が支払われるまで借地を留置することができます。
もっとも、残存期間を超えて存続するべき建物が無断再築がなされた場合には、裁判所は代金の全部又は一部の支払につき相当の期限を許与でき([[借地借家法第13条|借地借家法13条]]2項)、この場合にはその限度で代金支払が後履行となるため同時履行の抗弁権ないし留置権がその限度で縮減することとなります。また、借地権者の債務不履行によって契約が解除された場合については、建物買取請求権は認められません(最判昭和35年2月9日民集14巻1号108頁)。
=== 定期借地権 ===
定期借地権とは、契約期間の更新がなく、約定の期間が経過すれば必ず土地の返還がなされる借地権のことです。旧借地法による一律の強い存続保証は、一方で一旦土地を貸すと返ってこないという恐れを生じさせ、借地の供給に対する萎縮や借地権価格の高騰を生じさせたと考えられました。そこで、更新のない借地権を認め、これによって借地の供給量を増加させ、様々な経済的要請に応じることを目的に、借地借家法において定期借地権の制度が導入されました。定期借地権には以下の三種類があります。
; 一般定期借地権([[借地借家法第22条]])
: 50年以上の存続期間を定め、建物買取請求権を排除する特約をつけて設定される借地権です。存続期間終了時には借地を更地に戻して返還しなければなりません。またこの種の定期借地権の設定は、書面によらなければなりません。
; 事業用定期借地権等([[借地借家法第23条]])
: 事業用定期借地権等は、専ら事業の用に供する建物の所有を目的とする借地権であり、居住用の建物を目的とすることはできません。借地権の存続期間は10年以上50年未満であることが必要です。30年以上50年未満の存続期間を定めた場合は、9条及び16条の規定にかかわらず、契約の更新及び建物の築造による存続期間の延長がなく、また13条の規定による買取りの請求をしないこととする旨を定めることができます(同条1項)。10年以上30年未満の場合には、3条から8条、13条及び18条の規定は適用されません(同条2項)。この事業用借地契約は公正証書によってなされなければなりません(同条3項)。なお、平成19年12月31日までの事業用定期借地権の存続期間は10年以上20年以下であり、これについては旧法の適用があります。
; 建物譲渡特約付借地権([[借地借家法第24条]])
: 期間満了時に、借地上にある建物を相当の対価でもって借地権設定者に売却するとの特約を付した借地権です。存続期間は30年以上です(同条1項)。
=== 条件等の変更 ===
借地条件については原則として当事者の自由ですが、借地契約は長期間に渡るものであり、当初の予定と異なる事情の変更によって、借地条件が実情に合わないこととなることが考えられます。そこで借地借家法では、借地条件の変更についての規定を置き、問題の解決を図っています。
; 地代等増減請求権
: 地代が付相当な水準のまま維持されるという不都合を回避するため、借地借家法では、一定期間地代を増額しないという特約がある場合を除き、土地に対する租税等の増減、地価の上昇もしくは低下その他の経済事情の変動、あるいは近傍類似の土地の地代等に比較して不相当となった場合に、地代の改定を求める当事者の一方的意思表示によって、地代額を将来に向かって相当な金額に改定する権利が定められています([[借地借家法第11条]]1項)。これを地代等増減請求権といい、これは従前の地代等が不相当となったことを要件とする形成権です(大判昭和7年1月13日民集11巻7頁)。そこで、地代等を増減する意思表示が相手方に到達した日から、将来に向かって増減の効果が生じることとなります。
: もっとも、相手方の利益を保護するため、相当と認める地代の支払い・請求ができることが定められています。それによると、借地権設定者から地代の増額請求がなされた場合、借地権者は増額を正当とする裁判が確定するまでの間は、相当と認める額の地代を支払えば足ります。これにより借地権者は履行遅滞に陥らず、借地権設定者が受け取りを拒絶すればこれを供託することができます。ただし、地代を改定する裁判が確定した際に、既に支払った額に不足があるときには、借地権者はその不足額に年1割の利息を付して支払わなければなりません([[借地借家法第11条]]2項)。また逆に、借地権者から地代の減額請求がなされた場合には、借地権設定者は相当と認める額の地代の支払いを請求することができます。ただし地代を改定する裁判が確定したときに、請求した地代がこれを超過しているとき、借地権設定者はその超過額に年1割の受領時からの利息を付して返還しなければなりません([[借地借家法第11条]]3項)。
; 増改築禁止特約
: 借地契約において、増改築の禁止や制限を内容とする条項が設けられる場合があります。このような特約も有効なものですが、土地の利用上相当とすべき増改築について当事者間に協議が整わないとき、裁判所は借地権者の申し立てによりその増改築について、承諾に変わる許可を与えることができます([[借地借家法第17条]]1項)。
; 借地権の譲渡・転貸
: 賃貸人の承諾なしに不動産賃借権が譲渡・転貸された場合、賃貸借の頁で扱ったように、賃貸人は原則として賃貸借契約を解除できます。しかし、その第三者に賃借権を譲渡し、あるいは転貸をしても借地権設定者にとって不利になる虞がないにもかかわらず、借地権設定者が承諾をしないときには、裁判所は借地権者の申し立てにより承諾に代わる許可与えることができます([[借地借家法第19条]]1項)。
== 借家 ==
=== 対抗力 ===
建物賃借権は、賃借権の登記がない場合であっても、建物の引渡しがあったときには、その後にその建物について物権を取得したものに対抗できます([[借地借家法第31条]]1項)。これは、建物の占有移転という事実に建物賃借権の公示の機能があると考え、賃借人の保護を図るためのものです。
=== 存続期間と更新 ===
民法は、賃貸借期間の上限を20年と定めています([[民法第604条]])が、[[借地借家法第29条]]2項は建物賃貸借についてこの規定を適用しないものとしており、存続期間を20年以上と定めることも可能となっています。
また、借地借家法では、建物賃貸借についても法定更新の制度を定め、更新拒絶の際には正当事由を必要としています。
存続期間の定めのある建物賃貸借契約では、当事者が期間満了の1年前から6ヶ月前までの間に、相手方に対して更新しない旨の通知、または条件を変更しなければ更新をしない旨の通知をしなかったときは、従前の契約と同一の条件で契約を更新したものとみなされます([[借地借家法第26条]]1項本文)。また、例えこのような通知がなされたとしても、賃借人が期間満了後も建物の使用を継続し、賃貸人がこれに遅滞なく異議を述べなかったときは、従前の契約と同一の条件で契約を更新したものとみなされます([[借地借家法第26条]]2項)。法定更新が認められた場合、更新後の賃貸借は期間の定めのないものとなります([[借地借家法第26条]]1項但書)。そして、更新拒絶をする場合には、正当事由が必要です([[借地借家法第28条]])。
存続期間の定めのない建物賃貸借契約では、賃借人はいつでも解約申入れをすることができ、その場合申入れの日から3ヶ月を経過することで賃貸借契約は終了します([[民法第617条]]2号)。しかし賃貸人からの申入れについては借地借家法が特別の規定を定めており、賃貸人の解約申入れの日から6ヶ月を経過することによって終了するものとされています([[借地借家法第27条]]1項)。また、解約申入れには正当事由が必要であり([[借地借家法第28条]])、解約申入れがあり、かつ正当事由が認められる場合であっても、賃借人が期間満了後も縦者の使用を継続し、賃貸人がこれに遅滞なく異議を述べなかったときは、やはり従前の契約と同一の条件で契約を更新したものとみなされます([[借地借家法第27条]]2項による[[借地借家法第26条]]2項・3項の準用)。
=== 造作買取請求権 ===
建物賃貸借契約が終了した場合、建物の賃貸人の同意を得て建物に付加した畳、建具その他の造作があるときは、賃借人は賃貸人に対して、その造作を時価で買い取るべきことを請求することができます([[借地借家法第33条]])。これは形成権であり、賃借人の一方的意思表示により、相手方との間に売買契約が成立した場合と同様の効果が生じます。もっとも、この規定は任意規定であり、これを特約により排除することができます([[借地借家法第37条]]に借地借家法33条は含まれていません)。
判例(大判昭和7年9月30日民集11巻1859頁、最判昭和29年7月22日民集8巻7号1425頁)は、買取代金との同時履行関係に立つのは造作の引渡しであって建物の引渡しではないとして、造作買取代金の支払いと建物明渡しとは同時履行関係に立たないと判示しています。
=== 定期建物賃貸借 ===
建物賃貸借については特に期間制限なく、建物賃貸借契約について一定の期間を定め、契約の更新がないとの特約をすることができます。ただし、定期建物賃貸借契約は公正証書によるなど書面で行わなければならず([[借地借家法第38条]]1項)、かつ契約前に賃貸人は契約の更新がないことを記した書面を建物賃借人に交付して説明をしなければなりません([[借地借家法第38条]]2項・3項)。さらに、期間が1年以上である場合、賃貸人は期間満了の1年前から6ヶ月前までの間に、賃借人に期間の満了により建物の賃貸借が終了する旨の通知をしなければなりません。この通知が遅れた場合、通知の日から6ヶ月経過後に契約の終了を主張できることとなります([[借地借家法第38条]]4項)。
定期建物賃貸借においては賃借人も自由に解約申入れをすることはできず、ただ、居住の用に供する建物の賃貸借(床面積が二百平方メートル未満のもの)において、転勤、療養、親族の介護その他のやむを得ない事情により、建物の賃借人が建物を自己の生活の本拠として使用することが困難となったときには、賃借人は建物の賃貸借の解約の申入れをすることができます([[借地借家法第38条]]5項)。
=== 賃料増減請求権 ===
建物賃貸借においても、借地の賃料増減請求権と同様の賃料増減請求権が定められています([[借地借家法第32条]])。
==== サブリース ====
サブリースの定義について一定のものがあるわけではありませんが、概ね、不動産会社等がビルの所有者からビルを一括して借り、それを転貸借するという取引形態であり、ビル所有者は自らテナント募集などの事務・ビル管理を行うことなく安定した収益を上げ、不動産会社等は転貸料と賃借料の差額から収益を得るというものです。この際、土地の所有者は不動産会社などの勧めに従ってビルを建設して、これを不動産会社に賃貸することも多く、ビル建設の資金として不動産会社側から一定の金銭が支払われることもあります。そして、このようなサブリースでは、賃料を一定期間経過ごとに自動的に増額する、賃料自動増額特約が締結されることもあります。地価が上昇し、賃料も上昇していたときには所有者と不動産会社の双方が利潤を上げることができましたが、バブル崩壊後の不動産価値の下落とそれに伴う賃料の下落によって、問題が生じています。
このようなサブリースも賃貸借であり、借地借家法の適用があると考えられます(最判平成15年10月21日民集57巻9号1213頁、最判平成16年11月8日判時1883号52頁など)。しかし、このような場合に不動産会社との間で結んだ賃料自動増額特約の効力や賃料減額請求権の可否について、見解の対立があります。
一つの見解は、あくまで[[借地借家法第32条]]1項は強行規定であってこれと異なる契約に効力は認められず、たとえ賃料を自動的に増額する、あるいは減額しないといった契約が結ばれていたとしても、賃借人は賃料減額請求権を有しているという見解です。これに対して、契約締結時に賃借人が自らリスクを負担するものとしていたのであれば、特別の保護は必要なく、賃料自動増額特約の効力を認めるべきとの見解も主張されています。判例では、借地借家法第32条1項を強行法規とした上で、衡平の見地に照らし、契約締結時の事情として賃料が決定された経緯や賃料自動増額特約が付された事情なども考慮して、賃料減額請求の当否や相当賃料額が判断するものとされています。
(参照 [[w:借地借家法|借地借家法]]、[[w:サブリース|サブリース]])
== 参考 ==
*[[コンメンタール借地借家法]]
[[カテゴリ:借地借家法|*]]
i7i6y8dt13jys900vsexnprfs5g35o5
借地借家法第29条
0
12306
264529
61582
2024-11-30T02:02:27Z
Tomzo
248
264529
wikitext
text/x-wiki
[[法学]]>[[民事法]]>[[借地借家法]]>[[コンメンタール借地借家法]]
==条文==
(建物賃貸借の期間)
;第29条
#期間を一年未満とする建物の賃貸借は、期間の定めがない建物の賃貸借とみなす。
#[[民法第604条]] の規定は、建物の賃貸借については、適用しない。
==解説==
*民法第604条(賃貸借の存続期間)
(契約に期間の定めがある場合の存続期間)
{|
|-
|定期建物賃貸借以外||最短、最長とも特に規定はない。ただし、1年未満の期間を定めた場合は期間の定めがない契約と見なされる。
|-
|定期建物賃貸借||最短、最長とも特に規定はない。
|-
|借地借家法の適用対象外||20年を超えることができない(民法第604条)。
|}
==参照条文==
*[[借地借家法第3条]](借地権の存続期間)
==判例==
----
{{前後
|[[コンメンタール借地借家法|借地借家法]]
|[[コンメンタール借地借家法#3|第3章 借家]]<br>
[[コンメンタール借地借家法#3-1|第1節 建物賃貸借契約の更新等]]<br>
|[[借地借家法第28条]]<br>(建物賃貸借契約の更新拒絶等の要件)
|[[借地借家法第30条]]<br>(強行規定)
}}
{{stub|law}}
[[category:借地借家法|29]]
95xizy6crst51b7k1o51gktwqspboah
借地借家法第32条
0
14509
264535
227490
2024-11-30T02:39:02Z
Tomzo
248
264535
wikitext
text/x-wiki
[[法学]]>[[民事法]]>[[コンメンタール借地借家法]]
==条文==
(借賃増減請求権)
;第32条
# 建物の借賃が、土地若しくは建物に対する租税その他の負担の増減により、土地若しくは建物の価格の上昇若しくは低下その他の経済事情の変動により、又は近傍同種の建物の借賃に比較して不相当となったときは、契約の条件にかかわらず、当事者は、将来に向かって建物の借賃の額の増減を請求することができる。ただし、一定の期間建物の借賃を増額しない旨の特約がある場合には、その定めに従う。
# 建物の借賃の増額について当事者間に協議が調わないときは、その請求を受けた者は、増額を正当とする裁判が確定するまでは、相当と認める額の建物の借賃を支払うことをもって足りる。ただし、その裁判が確定した場合において、既に支払った額に不足があるときは、その不足額に年一割の割合による支払期後の利息を付してこれを支払わなければならない。
# 建物の借賃の減額について当事者間に協議が調わないときは、その請求を受けた者は、減額を正当とする裁判が確定するまでは、相当と認める額の建物の借賃の支払を請求することができる。ただし、その裁判が確定した場合において、既に支払を受けた額が正当とされた建物の借賃の額を超えるときは、その超過額に年一割の割合による受領の時からの利息を付してこれを返還しなければならない。
==解説==
==参照条文==
==判例==
#[https://www.courts.go.jp/app/hanrei_jp/detail2?id=52299 敷金請求本訴,賃料相当額確認請求反訴事件](最高裁判決 平成15年10月21日)
##'''いわゆるサブリース契約と借地借家法32条1項の適用の有無'''
##:不動産賃貸業等を営む甲が,乙が建築した建物で転貸事業を行うため,乙との間であらかじめ賃料額,その改定等についての協議を調え,その結果に基づき,乙からその建物を一括して賃料自動増額特約等の約定の下に賃借することを内容とする契約(いわゆるサブリース契約)についても,借地借家法32条1項の規定が適用される。
##'''いわゆるサブリース契約の当事者が借地借家法32条1項に基づく賃料減額請求をした場合にその請求の当否及び相当賃料額を判断するために考慮すべき事情'''
##:不動産賃貸業等を営む甲が,乙が建築した建物で転貸事業を行うため,乙との間であらかじめ賃料額,その改定等についての協議を調え,その結果に基づき,乙からその建物を一括して賃料自動増額特約等の約定の下に賃借することを内容とする契約(いわゆるサブリース契約)を締結した後,借地借家法32条1項に基づいて賃料減額の請求をした場合において,その請求の当否及び相当賃料額を判断するに当たっては,当事者が賃料額決定の要素とした事情その他諸般の事情を総合的に考慮すべきであり,同契約において賃料額が決定されるに至った経緯や賃料自動増額特約等が付されるに至った事情,とりわけ約定賃料額と当時の近傍同種の建物の賃料相場との関係,甲の転貸事業における収支予測にかかわる事情,乙の敷金及び融資を受けた建築資金の返済の予定にかかわる事情等をも考慮すべきである。
#[https://www.courts.go.jp/app/hanrei_jp/detail2?id=62463 建物賃料改定請求事件](最高裁判決 平成15年10月21日)
#;建物賃貸借契約に基づく使用収益の開始前に借地借家法32条1項に基づき賃料増減額請求をすることの可否
#:建物賃貸借契約の当事者は,契約に基づく建物の使用収益の開始前に,借地借家法32条1項に基づいて賃料の額の増減を求めることはできない。
#[https://www.courts.go.jp/app/hanrei_jp/detail2?id=62535 賃料請求本訴,同反訴事件](最高裁判決 平成17年3月10日)
#;賃借人の要望に沿って大型スーパーストアの店舗として使用するために建築され他の用途に転用することが困難である建物を目的とし3年ごとに賃料を増額する旨の特約を付した賃貸借契約について賃借人のした賃料減額請求権の行使を否定した原審の判断に違法があるとされた事例
#:賃借人の要望に沿って大型スーパーストアの店舗として使用するために建築され,他の用途に転用することが困難である建物について,賃貸人が将来にわたり安定した賃料収入を得ること等を目的として,3年ごとに賃料を増額する旨の特約を付した賃貸借契約が締結された場合において,賃料減額請求の当否を判断するに当たり,当初の合意賃料を維持することが公平を失し信義に反するというような特段の事情の有無により賃料減額請求の当否を判断すべきものとして,<u>専ら公租公課の上昇及び賃借人の経営状態のみを参酌し,土地建物の価格等の変動,近傍同種の建物の賃料相場等'''借地借家法32条1項所定の他の重要な事情'''を参酌しないまま</u>,賃借人のした賃料減額請求権の行使を否定した原審の判断には,違法がある。
#[https://www.courts.go.jp/app/hanrei_jp/detail2?id=35877 賃料減額確認請求本訴,同反訴事件](最高裁判決 平成20年2月29日)
#;賃料自動改定特約のある建物賃貸借契約の賃借人からの賃料減額請求の当否等を判断するに当たり,上記特約による改定前に当事者が現実に合意した直近の賃料を基にすることなく,上記特約によって増額された賃料を基にして,増額された日から当該請求の日までの間に限定して経済事情の変動等を考慮した原審の判断に違法があるとされた事例
#:賃料自動改定特約のある建物賃貸借契約の賃借人から借地借家法32条1項の規定に基づく賃料減額請求がされた場合において,当該請求の当否及び相当賃料額を判断するに当たり,上記特約による改定前に賃貸借契約の当事者が現実に合意した直近の賃料を基にして,その合意された日から当該請求の日までの間の経済事情の変動等を考慮しなければならないにもかかわらず,上記特約によって増額された賃料を基にして,増額前の経済事情の変動等を考慮の対象から除外し,増額された日から当該請求の日までの間に限定して,その間の経済事情の変動等を考慮した原審の判断には,違法がある。
#[https://www.courts.go.jp/app/hanrei_jp/detail2?id=84488 建物賃料増額確認請求事件](最高裁判決 平成26年9月25日)[[民事訴訟法第114条|民訴法114条]]1項,[[民事訴訟法第134条|民訴法134条]]
#;借地借家法32条1項の規定に基づく賃料増減請求により増減された賃料額の確認を求める訴訟の確定判決の既判力
#:借地借家法32条1項の規定に基づく賃料増減請求により増減された賃料額の確認を求める訴訟の確定判決の既判力は,原告が特定の期間の賃料額について確認を求めていると認められる特段の事情のない限り,前提である賃料増減請求の効果が生じた時点の賃料額に係る判断について生ずる。
----
{{前後
|[[コンメンタール借地借家法|借地借家法]]
|[[コンメンタール借地借家法#3|第3章 借家]]<br>
[[コンメンタール借地借家法#3-2|第2節 建物賃貸借の効力]]<br>
|[[借地借家法第31条]]<br>(建物賃貸借の対抗力等)
|[[借地借家法第33条]]<br>(造作買取請求権)
}}
{{stub|law}}
[[category:借地借家法|32]]
d973j866kwys7v1n6w6a616eq6igp6q
264536
264535
2024-11-30T02:51:53Z
Tomzo
248
/* 判例 */
264536
wikitext
text/x-wiki
[[法学]]>[[民事法]]>[[コンメンタール借地借家法]]
==条文==
(借賃増減請求権)
;第32条
# 建物の借賃が、土地若しくは建物に対する租税その他の負担の増減により、土地若しくは建物の価格の上昇若しくは低下その他の経済事情の変動により、又は近傍同種の建物の借賃に比較して不相当となったときは、契約の条件にかかわらず、当事者は、将来に向かって建物の借賃の額の増減を請求することができる。ただし、一定の期間建物の借賃を増額しない旨の特約がある場合には、その定めに従う。
# 建物の借賃の増額について当事者間に協議が調わないときは、その請求を受けた者は、増額を正当とする裁判が確定するまでは、相当と認める額の建物の借賃を支払うことをもって足りる。ただし、その裁判が確定した場合において、既に支払った額に不足があるときは、その不足額に年一割の割合による支払期後の利息を付してこれを支払わなければならない。
# 建物の借賃の減額について当事者間に協議が調わないときは、その請求を受けた者は、減額を正当とする裁判が確定するまでは、相当と認める額の建物の借賃の支払を請求することができる。ただし、その裁判が確定した場合において、既に支払を受けた額が正当とされた建物の借賃の額を超えるときは、その超過額に年一割の割合による受領の時からの利息を付してこれを返還しなければならない。
==解説==
==参照条文==
==判例==
#[https://www.courts.go.jp/app/hanrei_jp/detail2?id=52299 敷金請求本訴、賃料相当額確認請求反訴事件](最高裁判決 平成15年10月21日)
##'''いわゆるサブリース契約と借地借家法32条1項の適用の有無'''
##:不動産賃貸業等を営む甲が、乙が建築した建物で転貸事業を行うため、乙との間であらかじめ賃料額、その改定等についての協議を調え、その結果に基づき、乙からその建物を一括して賃料自動増額特約等の約定の下に賃借することを内容とする契約(いわゆるサブリース契約)についても、借地借家法32条1項の規定が適用される。
##'''いわゆるサブリース契約の当事者が借地借家法32条1項に基づく賃料減額請求をした場合にその請求の当否及び相当賃料額を判断するために考慮すべき事情'''
##:不動産賃貸業等を営む甲が、乙が建築した建物で転貸事業を行うため、乙との間であらかじめ賃料額、その改定等についての協議を調え、その結果に基づき、乙からその建物を一括して賃料自動増額特約等の約定の下に賃借することを内容とする契約(いわゆるサブリース契約)を締結した後、借地借家法32条1項に基づいて賃料減額の請求をした場合において、その請求の当否及び相当賃料額を判断するに当たっては、当事者が賃料額決定の要素とした事情その他諸般の事情を総合的に考慮すべきであり、同契約において賃料額が決定されるに至った経緯や賃料自動増額特約等が付されるに至った事情、とりわけ約定賃料額と当時の近傍同種の建物の賃料相場との関係、甲の転貸事業における収支予測にかかわる事情、乙の敷金及び融資を受けた建築資金の返済の予定にかかわる事情等をも考慮すべきである。
#[https://www.courts.go.jp/app/hanrei_jp/detail2?id=62463 建物賃料改定請求事件](最高裁判決 平成15年10月21日)
#;建物賃貸借契約に基づく使用収益の開始前に借地借家法32条1項に基づき賃料増減額請求をすることの可否
#:建物賃貸借契約の当事者は、契約に基づく建物の使用収益の開始前に、借地借家法32条1項に基づいて賃料の額の増減を求めることはできない。
#[https://www.courts.go.jp/app/hanrei_jp/detail2?id=62535 賃料請求本訴、同反訴事件](最高裁判決 平成17年3月10日)
#;賃借人の要望に沿って大型スーパーストアの店舗として使用するために建築され他の用途に転用することが困難である建物を目的とし3年ごとに賃料を増額する旨の特約を付した賃貸借契約について賃借人のした賃料減額請求権の行使を否定した原審の判断に違法があるとされた事例
#:賃借人の要望に沿って大型スーパーストアの店舗として使用するために建築され、他の用途に転用することが困難である建物について、賃貸人が将来にわたり安定した賃料収入を得ること等を目的として、3年ごとに賃料を増額する旨の特約を付した賃貸借契約が締結された場合において、賃料減額請求の当否を判断するに当たり、当初の合意賃料を維持することが公平を失し信義に反するというような特段の事情の有無により賃料減額請求の当否を判断すべきものとして、<u>専ら公租公課の上昇及び賃借人の経営状態のみを参酌し、土地建物の価格等の変動、近傍同種の建物の賃料相場等'''借地借家法32条1項所定の他の重要な事情'''を参酌しないまま</u>、賃借人のした賃料減額請求権の行使を否定した原審の判断には、違法がある。
#[https://www.courts.go.jp/app/hanrei_jp/detail2?id=35877 賃料減額確認請求本訴、同反訴事件](最高裁判決 平成20年2月29日)
#;賃料自動改定特約のある建物賃貸借契約の賃借人からの賃料減額請求の当否等を判断するに当たり、上記特約による改定前に当事者が現実に合意した直近の賃料を基にすることなく、上記特約によって増額された賃料を基にして、増額された日から当該請求の日までの間に限定して経済事情の変動等を考慮した原審の判断に違法があるとされた事例
#:賃料自動改定特約のある建物賃貸借契約の賃借人から借地借家法32条1項の規定に基づく賃料減額請求がされた場合において、当該請求の当否及び相当賃料額を判断するに当たり、上記特約による改定前に賃貸借契約の当事者が現実に合意した直近の賃料を基にして、その合意された日から当該請求の日までの間の経済事情の変動等を考慮しなければならないにもかかわらず、上記特約によって増額された賃料を基にして、増額前の経済事情の変動等を考慮の対象から除外し、増額された日から当該請求の日までの間に限定して、その間の経済事情の変動等を考慮した原審の判断には、違法がある。
#[https://www.courts.go.jp/app/hanrei_jp/detail2?id=84488 建物賃料増額確認請求事件](最高裁判決 平成26年9月25日)[[民事訴訟法第114条|民訴法114条]]1項、[[民事訴訟法第134条|民訴法134条]]
#;借地借家法32条1項の規定に基づく賃料増減請求により増減された賃料額の確認を求める訴訟の確定判決の既判力
#:借地借家法32条1項の規定に基づく賃料増減請求により増減された賃料額の確認を求める訴訟の確定判決の既判力は、原告が特定の期間の賃料額について確認を求めていると認められる特段の事情のない限り、前提である賃料増減請求の効果が生じた時点の賃料額に係る判断について生ずる。
----
{{前後
|[[コンメンタール借地借家法|借地借家法]]
|[[コンメンタール借地借家法#3|第3章 借家]]<br>
[[コンメンタール借地借家法#3-2|第2節 建物賃貸借の効力]]<br>
|[[借地借家法第31条]]<br>(建物賃貸借の対抗力等)
|[[借地借家法第33条]]<br>(造作買取請求権)
}}
{{stub|law}}
[[category:借地借家法|32]]
rphwfnogqy5en8yrhtpaqma6e15xhea
借地借家法第30条
0
16512
264530
132803
2024-11-30T02:03:25Z
Tomzo
248
264530
wikitext
text/x-wiki
[[法学]]>[[民事法]]>[[借地借家法]]>[[コンメンタール借地借家法]]
==条文==
(強行規定)
;第30条
:この節の規定に反する特約で建物の賃借人に不利なものは、無効とする。
==解説==
==参照条文==
==判例==
----
{{前後
|[[コンメンタール借地借家法|借地借家法]]
|[[コンメンタール借地借家法#3|第3章 借家]]<br>
[[コンメンタール借地借家法#3-1|第1節 建物賃貸借契約の更新等]]<br>
|[[借地借家法第29条]]<br>(建物賃貸借の期間)
|[[借地借家法第31条]]<br>(建物賃貸借の対抗力等)
}}
{{stub|law}}
[[category:借地借家法|30]]
swjqc27i0yp3d8abywqen7mnbsway9k
高校化学 有機化合物と人間生活
0
18231
264518
264210
2024-11-29T15:38:18Z
Nermer314
62933
264518
wikitext
text/x-wiki
{{pathnav|高等学校の学習|高等学校理科|高等学校 化学|pagename=有機化合物と人間生活|frame=1|small=1}}
この章での色の定義については、まずは直感的に理解していれば良い。(正確に色覚を説明すると生物学など他分野が絡み、広範かつ専門的になる。)読者には、まずは色素の分子構造と電磁波との関係について、理解をしてもらいたい。
== 物質の色 ==
[[Image:Linear visible spectrum.svg]]
{| class="wikitable" style="float:right; text-align:right; margin:0px 0px 7px 7px;"
|-
!色
!波長
|-
| style="background:#ccb0f4;"|'''紫'''
|380-450 nm
|-
| style="background:#b0b0f4;"|'''青'''
|450-485 nm
|-
| style="background:#b0f4f4;"|'''水色'''
|485-500 nm
|-
| style="background:#b0f4b0;"|'''緑'''
|500-565 nm
|-
| style="background:#f4f4b0;"|'''黄色'''
|565-590 nm
|-
| style="background:#f4ccb0;"|'''橙色'''
|590-625 nm
|-
| style="background:#f4b0b0;"|'''赤'''
|625-780 nm
|}
[[File:MunsellColorWheel.svg|200px|thumb|円の正反対に位置する色が補色]]
材料に添加した場合に色を加える物質を'''色素'''(coloring material)という。繊維に染色できる色素を'''染料'''(dye stuff)という。
白色光で照らされた物体は、可視光の特定の波長を吸収して、残りの波長の光を反射する。したがって物質が吸収する色と、反射光は、反対の色になる。
たとえば、赤色の物体は、光源の白色光から、青色や緑色や紫色などの、赤色以外の色の波長を吸収して、赤色のみを反射したので、ヒトの目には赤色に見えるのである。
赤色に対する、青緑色のように、反対側の色を'''補色'''という。赤は青緑の補色である。同様に青緑は赤の補色である。
白色光に照らされた物質に色を感じる仕組みは、物質が吸収した色の補色を、色として感じるのである。
=== 色の知覚 ===
[[ファイル:Cones SMJ2 E.svg|thumb|280px|正規化されたヒトの'''錐体細胞''' (S, M, L) の分光応答度]]
色は、網膜に存在するS錐体、M錐体、L錐体の3種類の錐体細胞により知覚される。S錐体は可視光のうち短い波長(440 nm付近)に反応し、M錐体は中程度の波長(540 nm付近)に反応し、L錐体は長い波長(560 nm付近)に反応する<ref>それぞれ、Short, Medium, Longの頭文字に由来する。</ref>。色の三原色に対応して、S錐体が青、M錐体が緑、L錐体が赤の知覚に関わると思われることもあるが<ref>かつては、B錐体、G錐体、R錐体と呼ばれていた。</ref>これは適切ではない。実際、L錐体のピーク波長 560 nmは黄緑であって、赤ではない。
S錐体、M錐体、L錐体が等しく刺激されると、白(あるいは灰)に感じる。また、赤と青緑の光を混合すると白に感じる。これは、赤がL錐体を主に刺激し、青緑がS錐体、M錐体を刺激するから、それらを混合した光は、S錐体、M錐体、L錐体をほぼ等しく刺激するため白に感じるためである。このように、2つの色の光を混ぜたとき、白に感じる関係を補色という。
また、赤紫の単色光は存在しない。これは、赤紫がL錐体とS錐体のみが反応して知覚される色だからである。このような光は、紫と赤の単色光の混合色であるが、単色光ではありえない。光のスペクトルの両端に赤紫をつなげることで、色を円形に配置することができる。ここで、補色の関係にある色が相対するように配置すると、色相環が得られる。{{clear}}
== 色素 ==
色素には炭素化合物などの有機化合物からなる有機系の色素と、無機化合物からなる無機系の色素がある。本節では有機色素について説明する。
化合物に色を付けるには、その構造中に可視光を吸収できる官能基が必要である。可視光を吸収できて、物質に色を付けられる官能基を'''発色団'''(chromophore)という。有機色素の発色団は、-C=Oや、-N=N- などのように共役ニ重結合を持った官能基である。共役ニ重結合によりπ電子が動けるようになっているので、電磁波と電子が相互作用ができるようになり、外部からの光を吸収できるようになっている。(たとえば、金属などの自由電子を持つ物質は不透明だったのを思い出そう。)
発色団以外の官能基で、発色団の作用を強めたり、親水性を高め染色しやすくする官能基を'''助色団'''(auxochrome)という。助色団には、<chem>-OH</chem>や<chem>-COOH</chem> や<chem>-NH2</chem> などがある。
;発色団
: >C=C<
: >C=O
: >C=N- (ケチミド基)
: -N=N- (アゾ基)
: -N=O (ニトロソ基)
;助色団
: -OH (ベンゼン環につき、フェノール化している場合が多い。)
: -NH<sub>2</sub>
: -COOH (極性を持ち、親水性を与える。)
: -SO<sub>3</sub>H (極性を持ち、親水性を与える。)
: -X (ハロゲン原子)
== アゾ化合物 ==
アゾ基 <chem>-N # N -</chem> を発色団として持った染料をアゾ染料(azo dye)という。アゾ染料としてコンゴーレッド(congo red)やメチルオレンジ、メチルレッドなどが有る。コンゴレッドやメチルオレンジなどは染料の他の用途でも、pH指示薬として用いられることがある。
=== ジアゾカップリング ===
;ジアゾ化
アニリン <chem>C6H5NH2</chem> などの芳香族アミンを希塩酸に溶かしたのち、亜硝酸ナトリウム水溶液を加える事で、ジアゾニウム塩を作ることができる。このジアゾニウム塩が、アゾ染料の原料となる。また、芳香族アミンをジアゾニウム塩にする処理を'''ジアゾ化'''(diazotization)という。
アニリン <chem>C6H5NH2</chem> をジアゾ化する場合は、まずアニリンを希塩酸に溶かしてから、亜硝酸ナトリウムを加えることで、塩化ベンゼンジアゾニウム <chem>C6H5N2Cl</chem> ができる。
ジアゾニウム塩は水に溶け、陽イオンのベンゼンジアゾニウムイオン <chem>[C6H5-N#N]^+</chem> と、陰イオンの塩素イオン <chem>Cl-</chem>とに電離する。なお、<chem>-N # N -</chem>の左側のNは4価である。これはイオン化のためである。
この4価を意識した構造式の書き方として、塩を <chem>C6H5-N^+ # N-Cl^-</chem> と書いたり、陽イオンを <chem>C6H5-N^+ # N</chem> と書く場合もある。
:<chem>C6H5-NH2 + 2 HCl + NaNO2 -> [C6H5-N=N]^+ Cl^- + NaCl + 2H2O</chem>
;カップリング
この塩化ベンゼンジアゾニウムC<sub>6</sub>H<sub>5</sub>-N≡N-Clと、ナトリウムフェノキソドC<sub>6</sub>H<sub>5</sub>-ONaとから合成によってp-フェニルアゾフェノールC<sub>6</sub>H<sub>5</sub>-N≡N-C<sub>6</sub>H<sub>4</sub>-OHが作られる。
:<chem>C6H5-N=Cl + C6H5-ONa -> C6H5-N=N-C6H4-OH</chem>
これは発色団-N≡N-と、助色団-OHを持つように、色素や染料として使えることから、一般に染料としてp-フェニルアゾフェノールは用いられる。これは共役二重結合が2個のベンゼン環と窒素部分とつながっていて、共役二重結合が長い。
フェノキシドとして用いたフェノールの代わりに、ナフトールやアニリンなどでもカップリング反応は可能である。
== 染料の分類 ==
[[File:Persicaria tinctoria bergianska.JPG|thumb|right|200px|アイの葉]]
天然に作られた色素の染料を'''天然染料'''(natural dye)と言い、合成によって得られた色素を用いた染料を'''合成染料'''(synthetic dye)という。
* 天然染料
** 植物染料
*** '''例'''
*** アカネ(茜)・・・・・・植物のアカネの根から色素の'''アリザニン'''という紅色の色素が得られる。
*** アイ(藍)・・・・・・植物のの葉から、色素の'''インジゴ'''という藍色の色素が得られる。
** 動物染料
*** 例
*** コチニール・・・・・・サボテンに寄生する虫のコチニール虫から色素の'''カルミン酸'''という赤色の色素がとれる。この色素をコチニール色素とも言う。
*** 貝紫・・・・・・アクギガイ科の貝の分泌物から色素の'''ジブロモインジゴ'''という紫色の色素が得られる。
<gallery widths="250px">
Image:Alizarin chemical structure.png|アリザニン
ファイル:Indigo skeletal.svg|インジゴ
</gallery>
== 医薬品 ==
一般に、ヒトや動物の病気を治すために使用する物質を、医薬品という。
医薬品が、それを使用した生物におよぼす変化を'''薬理作用'''という。
一般に、医薬品は体内でさまざまな作用を起こす。このうち、治療の目的に沿った作用を'''主作用'''といい、それ以外の作用を'''副作用'''という。
=== 歴史 ===
[[ファイル:Papaver_somniferum_2021_G3.jpg|サムネイル|ケシの実を傷つけて出た液体を乾燥させたものがアヘンである。]]
人類は、古代から天然の植物などから医薬品として機能するものを採取して使用してきた。このような天然由来の医薬品を'''{{Ruby|生薬|しょうやく}}'''という。
ケシの実から取れる果汁を乾燥させたアヘンも古代から知られている生薬の一つである。アヘンは、紀元前1500年のエジプトでは鎮痛剤として利用されていた。
19世紀初頭、アヘンから、麻酔・鎮痛薬の'''モルヒネ'''が抽出された。
19世紀後半に、いくつかの薬の化学構造が解明され、これらの成果をもとに、いくつかの薬品が合成された。
1910年ドイツのパウル・エールリヒと{{Ruby|秦佐八郎|はたさはちろう}}によって梅毒の治療薬サルバルサンがつくられた。
現在では、人工的に化学合成された有機化合物が、医薬品として多く使用されている。
== サリチル酸系の医薬品 ==
古くから、ヤナギの樹皮には解熱作用や鎮痛作用が存在することが知られていた。これは、ヤナギの樹皮にあるサリシンが体内で加水分解されてサリチル酸を生じるためである。
19世紀初頭に、化学分析によって、サリシンや、それから生じる'''サリチル酸'''の存在が知られ、解明されていった。
19世紀に、サリチル酸は解熱鎮痛薬として、さかんに使われていたが、胃に悪影響を与えることが、しだいに分かっていった。そのため、19世紀後半ごろには副作用の弱い'''アセチルサリチル酸'''が開発され使用されるようになった。
アセチルサリチル酸は1898年にドイツで'''アスピリン'''の商品名で医薬品として売り出され、現在でも解熱鎮痛薬としてアスピリンの名前で世界各地で売られている。(バファリンにも、アセチルサリチル酸が含まれている。)
現在では、サリチル酸系の多くの医薬品が存在している。
[[ファイル:アセチルサリチル酸.svg|サムネイル|アセチルサリチル酸]]
また、サリチル酸にメタノールを反応させて作ることのできる'''サリチル酸メチル'''は、消炎鎮痛薬(筋肉痛などを抑える薬)として用いられている。たとえば、『サロンパス』などのように、サリチル酸メチルは湿布薬として用いられていたりする。
なお、これらサリチル酸系の解熱薬は、けっして細菌などを攻撃してるのではなく、熱や炎症などの症状をやわらげるだけである。このように、病原菌を攻撃せず、症状をやわらげる事が主な作用の医薬品を、'''対症療法薬'''という。
またなお、サリチル酸メチルは揮発性の液体である。
* 参考: プロスタグランジンとサリチル酸系医薬品との関係
人体で、アセチルサリチル酸の薬が炎症や発熱などを抑える仕組みは、人体でケガなどの異常があったときに炎症などを起こして回復させようとする体内物質のプロスタグランジン(prostaglandin、略称:PG)という物質の合成を妨害するからである。<!-- (※ プロスタグランジンは検定教科書(高校理科の化学)の範囲外だが、文英堂シグマベストの高校化学参考書などに、プロスタグランジンとアセチルサリチル酸との関係の解説がある。) -->
よって、アセチルサリチル酸は、けっして、おおもとのケガを治すわけではないし、けっして病原菌を退治するわけでもない。
このプロスタグランジンは、炎症以外にも、人体に必要なさまざまな現象で関わってくるので、よってプロスタグランジンの合成が阻害されると、さまざまな副作用が起こりうるのである。
プロスタグランジンは、脂肪酸を原料としていて、体内で合成される生理活性物質である。プロスタグランジンは、いわば、ホルモンのようなものである(詳しい説明は高校の範囲を超えるので省略)。
== アミド系の医薬品 ==
[[ファイル:Acetanilide2.svg|サムネイル|アセトアニリド]]
アニリンから得られる'''アセトアニリド'''にも解熱鎮痛作用があるが、副作用が重いため、現在は使用されていない。
かわりに、アセトアニリドの誘導体である'''アセトアミノフェン'''(p-アセトアミドフェノール)が、風邪薬などに含まれてる。
[[ファイル:Acetanilide3.svg|左|サムネイル|アセトアニリド]]
[[ファイル:Acetaminophen.svg|中央|サムネイル|アセトアミノフェン(p-ヒドロキシアセトアニリド)]]
{{-}}
== 化学療法薬 ==
=== サルファ剤 ===
1939年にドイツのドーマクが、アゾ染料の一種のプロントジルに、細菌の増殖を阻害する作用があることを見つけた。
のちに、プロントジルから生じるスルファニルアミド [[ファイル:スルファニルアミド.svg|200x200ピクセル]] に、細菌の増殖をおさえる作用があることが分かった。これは、細菌が発育に必要な葉酸を合成するさいの酵素を阻害するからである。
細菌はp-アミノ安息香酸 [[ファイル:P-aminobenzoic_acid.svg|200x200ピクセル]]から葉酸を合成しているが、スルファニルアミドはp-アミノ安息香酸に似た構造を持ってるため、酵素を阻害する。
現在では、一般に、スルファニルアミドの骨格をもつ抗菌剤を、硫黄を元素にもつことから、'''サルファ剤'''(salfa drug)という。
=== 抗生物質 ===
微生物がつくりあげる化学物質で、ほかの微生物や細菌を殺したり、ほかの微生物や細菌の増殖を阻害したりする作用(抗菌作用)のあるものを'''抗生物質'''(antibiotics)という。
1929年にイギリスのフレミングは、アオカビから取れる物質に、このような抗菌作用があることを見つけ、この物質に'''ペニシリン'''(Penicillin)と名付けた。
: (※ 暗記は不要: )パンなどに生える青色のカビも通常、アオカビである<ref>David P.Clark 原著『クラーク分子生物学』、田沼靖一 監訳、平成19年12月10日 発行、丸善、P36</ref>。
のちに、ペニシリンは、細菌のもつ細胞壁の合成を阻害するため、抗菌作用を示すことが分かった。
細菌は突然変異により、抗生物質の効かない細菌が生まれることがある。このような細菌を'''耐性菌'''という。 抗生物質を無闇に使い続けると、このような抗生物質のきかない微生物だけを残して増やしてしまう。
ペニシリンの効かない耐性菌もすでに存在しており、そのような病原菌には抗生物質メチシリンや抗生物質バンコマイシンが使われることがあるが、そのメチシリンの効かない耐性菌MRSAや、バンコマイシンの効かない耐性菌VRSAなどの耐性菌も出現しており、医療現場では大きな問題になってる。
このため、抗生物質ばかりに頼らず、手洗いや消毒などをきちんと徹底したりすることが、求められてる。
なお、ストレプトマイシンは、結核にきく抗生物質である。土壌細菌のつくる物質からストレプトマイシンが発見された。
サルファ剤や抗生物質のように、病気をおこす細菌や微生物を、直接、細菌への破壊的な作用を起こすことで、病気を治療する医薬品を'''化学療法薬'''という。
* ペニシリンの作用の仕組み
[[ファイル:ペニシリンG_化学構造.svg|サムネイル|450x450ピクセル|ペニシリンG]]
ペニシリンG の構造のβラクタムという部分が、細菌の細胞壁の合成をする酵素を阻害する。
[[カテゴリ:高等学校化学|ゆうきかこうふつ]]
[[Category:高等学校教育]]
[[Category:化学]]
cxjy7z8cue48lwbhtmrd7cj7kdvxe8f
高等学校歴史総合
0
21706
264526
264512
2024-11-29T22:59:55Z
Kwawe
68789
/* 冷戦終結後の世界 */
264526
wikitext
text/x-wiki
[[小学校・中学校・高等学校の学習]]>[[高等学校の学習]]>[[高等学校地理歴史]]>高等学校歴史総合
{{進捗状況}} 「'''歴史総合'''」は標準単位数2単位で'''必修科目'''です。
=== 注意事項 ===
# 目次の項目、本文内容ともに[https://www.shimizushoin.co.jp/info_kyo/rekishisougou/index.html 清水書院『私たちの歴史総合』]【歴総705】に合わせています。
# なお、古代と中世に関しては、当科目では扱っていないので、[[高等学校世界史探究|世界史探究]]と[[高等学校日本史探究|日本史探究]]の学習内容を見てください。
== 第1編 歴史の扉 ==
=== 第1章 歴史と私たち ===
* [[高等学校歴史総合/日本とスポーツの歴史|日本とスポーツの歴史]]{{進捗|100%|2024-11-22}}(スポーツ史)
=== 第2章 歴史の特質と資料 ===
* 8月15日とそれぞれの「終戦」{{進捗|00%|2022-12-18}}(玉音放送)
== 第2編 近代化と私たち ==
=== 第1章 生活や社会の変化を読み取ってみよう(未記述) ===
=== 第2章 結びつく世界と日本 ===
==== 18世紀までの世界 ====
* [[高等学校歴史総合/近世の日本と世界|近世の日本と世界]]{{進捗|100%|2024-11-23}}(日本の「鎖国」と東アジアの交易、近世東アジアの国際秩序)
* [[高等学校歴史総合/18世紀の中国とアジア貿易|18世紀の中国とアジア貿易]]{{進捗|00%|2023-02-12}}(清の繁栄、清と近隣諸国とのつながり)
* [[高等学校歴史総合/18世紀のイギリス・アジア・アフリカ|18世紀のイギリス・アジア・アフリカ]]{{進捗|00%|2023-02-12}}(ヨーロッパの世界進出と大西洋三角貿易、世界経済の覇権を握ったイギリス)
* もっと知りたい 海を渡った日本産陶磁器{{進捗|00%|2023-12-24}}(陶磁器の歴史)
* もっと知りたい 琉球と蝦夷地{{進捗|00%|2022-12-25}}(琉球王国、蝦夷地)
==== 工業化と世界市場の形成 ====
* [[高等学校歴史総合/産業革命による経済発展と社会の変化|産業革命による経済発展と社会の変化]]{{進捗|00%|2022-11-25}}(産業革命、資本主義社会)
* [[高等学校歴史総合/世界市場の形成とイギリスによるアジア進出|世界市場の形成とイギリスによるアジア進出]]{{進捗|00%|2023-02-25}}(イギリスによる世界市場の形成、イギリスのアジア進出 )
* [[高等学校歴史総合/日本の開国とその影響|日本の開国とその影響]]{{進捗|00%|2022-11-21}}(日本の開国・開港、交通革命の進展と東アジア)
* 歴史のなかの16歳 工女と工場法{{進捗|00%|2022-12-25}}(工場法など)
* [[高等学校歴史総合/もっと知りたい 産業革命とブラスバンド|もっと知りたい 産業革命とブラスバンド]]{{進捗|100%|2024-11-23}}(金管楽器の歴史)
=== 第3章 国民国家と明治維新 ===
==== 国民国家と立憲体制 ====
* 二つの市民革命と近代民主主義社会の成立{{進捗|00%|2022-11-22}}(アメリカ独立革命、フランス革命、ナポレオン)
* 国民統合とナショナリズム{{進捗|00%|2022-11-23}}(19世紀前半のヨーロッパ諸国、ドイツ統一、南北戦争など)
* 明治維新期の日本と世界{{進捗|100%|2023-02-19}}(明治新政府の成立、近代化と東アジア)
* 近代国家への移行と憲法の制定{{進捗|00%|2022-11-24}}(大日本帝国憲法、条約改正の実現)
* もっと知りたい 国境の過去・現在・未来{{進捗|00%|2022-12-26}}(近代国家と領土画定など)
* もっと知りたい 女王と天皇 理想の家族{{進捗|00%|2022-02-12}}(近代の天皇史)
==== 帝国主義とアジア・アフリカの変容 ====
* 列強による帝国主義{{進捗|00%|2023-02-17}}(第2次産業革命と帝国主義、欧米諸国の帝国主義政策)
* 帝国主義がアジア・アフリカにもたらしたもの{{進捗|00%|2023-02-18}}(列強のアフリカ分割、西アジア諸国の改革など)
* 日清戦争とその影響{{進捗|00%|2023-02-15}}(日清戦争、東アジアの構造変動)
* 日露戦争{{進捗|00%|2022-11-22}}(義和団事件、日露戦争、朝鮮の植民地化、辛亥革命)
* もっと知りたい 近代の博覧会{{進捗|00%|2022-12-24}}(万国博覧会の歴史)
* [[高等学校歴史総合/もっと知りたい ペストと感染症|もっと知りたい ペストと感染症]]{{進捗|75%|2024-11-24}}(ペストの大流行ほか)
=== 第4章 近代化と現代的な諸課題 ===
* 鉄道建設{{進捗|00%|2023-03-02}}
== 第3編 国際秩序の変化や大衆化と私たち ==
=== 第1章 生活や社会の変化を読み取ってみよう(未記述) ===
=== 第2章 第一次世界大戦と大衆社会 ===
==== 第一次世界大戦と国際社会 ====
* [[高等学校歴史総合/第一次世界大戦|第一次世界大戦]]{{進捗|100%|2024-11-25}}(第一次世界大戦)
* [[高等学校歴史総合/社会主義革命|社会主義革命]]{{進捗|100%|2024-11-27}}(ロシア革命、コミンテルン、ソビエト社会主義共和国連邦)
* [[高等学校歴史総合/国際協調体制|国際協調体制]]{{進捗|100%|2024-11-23}}(ヴェルサイユ体制とワシントン体制、国際協調の高まり)
* [[高等学校歴史総合/アジアの民族運動|アジアの民族運動]]{{進捗|00%|2023-01-27}}(アジアの経済成長、東アジアの民族運動、インド・東南アジア・西アジアの民族運動)
* もっと知りたい ユダヤ人のパレスチナ移住とパレスチナ分割{{進捗|00%|2022-12-30}}(パレスチナの歴史、シオニズム運動)
* もっと知りたい 浅川巧 朝鮮の人々とともに生きた日本人{{進捗|00%|2022-12-30}}(浅川兄弟)
==== 1920年代の世界と大衆の時代の到来 ====
* [[高等学校歴史総合/大衆の政治参加|大衆の政治参加]]{{進捗|00%|2023-01-08}}(世界史上の民衆運動、大正デモクラシーと大衆の政治参加)
* [[高等学校歴史総合/女性の社会参加|女性の社会参加]]{{進捗|00%|2023-02-23}}(女性の社会進出、日本の大正期の女性)
* [[高等学校歴史総合/大衆社会の形成|大衆社会の形成]]{{進捗|100%|2024-11-27}}(大衆社会の出現、1920年代のアメリカ、日本の大衆社会)
* もっと知りたい 映画と「大衆化」{{進捗|00%|2022-12-28}}(映画の歴史)
* もっと知りたい オリンピックの歩み{{進捗|00%|2022-12-28}}(オリンピックの歴史)
=== 第3章 経済危機と第二次世界大戦 ===
==== 国際協調の挫折と2度目の世界大戦 ====
* 世界恐慌{{進捗|00%|2022-11-24}}(昭和恐慌、ブロック経済圏)
* ファシズムの台頭{{進捗|00%|2022-12-31}}(ファシズム、ナチ党の政権掌握)
* [[高等学校歴史総合/日本の大陸進出|日本の大陸進出]]{{進捗|00%|2022-12-08}}(満州事変、日中戦争)
* 第二次世界大戦{{進捗|00%|2022-11-16}}(ドイツの拡大、第二次世界大戦、大量殺戮と民衆の抵抗)
* もっと知りたい リンゲルブルム・アーカイヴと『アンネの日記』{{進捗|00%|2022-12-25}}(アンネ・フランク)
* 歴史のなかの16歳 満蒙開拓青少年義勇軍{{進捗|100%|2022-11-21}}(満州農業移民、満蒙開拓青少年義勇軍、「鍬の戦士」)
==== 世界大戦がもたらしたもの ====
* アジア太平洋戦争{{進捗|100%|2023-01-29}}(日米交渉の挫折と開戦、日本のアジア支配、戦争の被害と加害)
* [[高等学校歴史総合/戦争が変えた人々のくらし|戦争が変えた人々のくらし]]{{進捗|00%|2023-02-19}}(マス・メディアの普及と情報や生活の画一化、国民の組織化と戦時動員ほか)
* 戦後世界の新たな枠組み{{進捗|00%|2023-01-29}}(戦後構想と大戦の終結、冷戦の開始とドイツの分断、国際連合の成立)
* 敗戦後の日本とアジア{{進捗|00%|2023-02-19}}(日本の戦後改革、大衆は敗戦をどう生きたか、冷戦と日本の独立)
* 冷戦下の東アジア{{進捗|00%|2023-02-19}}(戦後の中国、朝鮮半島の南北分断、現代の朝鮮半島と台湾)
* もっと知りたい 戦争を「記憶」するということ{{進捗|00%|2022-12-24}}(明日の神話ほか)
* もっと知りたい 核と原子力エネルギー{{進捗|00%|2023-02-09}}(原子力の平和利用、原子力エネルギーの普及と課題)
=== 第4章 国際秩序の変化や大衆化と現代的な諸課題 ===
* ナショナリズム{{進捗|00%|2022-03-08}}[発展講義]
== 第4編 グローバル化と私たち ==
=== 第1章 生活や社会の変化を読み取ってみよう(未記述) ===
=== 第2章 冷戦と世界経済 ===
==== 冷戦と国際政治 ====
* 脱植民地化とアジア・アフリカ諸国{{進捗|00%|2023-01-25}}(アジア・アフリカ諸国の独立、第三世界の形成と連帯)
* 冷戦下の地域紛争{{進捗|00%|2023-01-26}}(ベトナム戦争、中東戦争)
* 先進国の政治と社会運動{{進捗|00%|2023-01-29}}(西側諸国と福祉国家政策、国境をこえる社会運動)
* 核兵器の脅威と核軍縮{{進捗|00%|2023-01-29}}(核拡散と核兵器反対運動、核軍縮の取り組みと課題)
==== 世界経済の拡大と日本 ====
* [[高等学校歴史総合/西ヨーロッパ・東南アジアの地域連携|西ヨーロッパ・東南アジアの地域連携]]{{進捗|00%|2022-12-31}}(西ヨーロッパ統合への動き、東南アジア諸国の動き)
* [[高等学校歴史総合/戦後の日本とアジア諸国との関係|戦後の日本とアジア諸国との関係]]{{進捗|00%|2023-01-01}}(日本の国連加盟、アジア諸国との国交回復、沖縄の本土復帰)
* [[高等学校歴史総合/高度経済成長|高度経済成長]]{{進捗|00%|2023-01-01}}(日本と西ドイツの経済成長、高度経済成長と人々の生活、成長のもたらした課題)
* もっと知りたい グローバリゼーションとストリートダンス{{進捗|00%|2022-12-27}}(ダンスの歴史)
* 歴史のなかの16歳 集団就職 「金の卵」たちの時代{{進捗|00%|2023-02-09}}(集団就職列車など)
=== 第3章 世界秩序の変容と日本 ===
==== 市場経済の変容と冷戦の終結 ====
* 石油危機と価値観の転換{{進捗|00%|2023-02-09}}(戦後経済の転換、石油危機と日本、価値観の転換)
* [[高等学校歴史総合/アジアの成長|アジアの成長]]{{進捗|00%|2023-02-13}}(アジア諸国の成長、第三世界の多様化、日本の経済大国化)
* [[高等学校歴史総合/冷戦の終結|冷戦の終結]]{{進捗|00%|2023-02-12}}(社会主義世界の変容、冷戦の終結とソ連の崩壊、冷戦後の地域紛争と日本)
==== 冷戦終結後の世界 ====
* [[高等学校歴史総合/民主化の進展と冷戦終結後の日本|民主化の進展と冷戦終結後の日本]]{{進捗|00%|2022-12-31}}(民主化の進展と課題、日本の政治の展開)
* [[高等学校歴史総合/市場開放と経済の自由化|市場開放と経済の自由化]]{{進捗|100%|2024-11-29}}(新自由主義の台頭、経済のグローバル化と新たな国際経済組織)
* [[高等学校歴史総合/地域統合の進展と課題|地域統合の進展と課題]]{{進捗|100%|2024-11-29}}(地域統合の拡大、地域統合の課題)
* [[高等学校歴史総合/情報通信技術の発達|情報通信技術の発達]]{{進捗|100%|2024-11-27}}(情報通信技術の発達と社会の変化、情報化社会とその課題)
* [[高等学校歴史総合/冷戦終結後の紛争と平和への取り組み|冷戦終結後の紛争と平和への取り組み]]{{進捗|25%|2024-11-30}}(冷戦終結後の紛争、紛争の解決と国際社会の役割)
* もっと知りたい 災害と私たち{{進捗|00%|2022-11-21}}(阪神淡路大震災、東日本大震災)
* もっと知りたい 中東の少数派クルド人{{進捗|00%|2022-12-27}}(国民国家の中の少数派、クルド人問題の展開)
=== 第4章 現代的な諸課題の形成と展望 ===
* 移民{{進捗|00%|2023-02-22}}(近代の移民史)
== 近現代史関連用語解説・参考 ==
* 近現代史関連用語解説{{進捗|00%|2023-02-22}}
* 途上国から見た国際関係理論{{進捗|00%|2023-03-05}}[発展講義(イマニュエル・ウォーラースティンなど)]
[[カテゴリ:歴史|こうとうかつこうれきしそうこう]]
[[カテゴリ:高等学校歴史総合|*]]
th7k1qx2s5khb6x0wj9xfsndopafucq
高等学校政治経済/大日本帝国憲法と日本国憲法
0
22289
264519
264114
2024-11-29T16:18:09Z
しゃちのアカウント
46198
本分野における記述として不適切なものを削除。詳細はノートページに書いておきます。
264519
wikitext
text/x-wiki
== 明治憲法 ==
大日本帝国憲法(明治憲法)は、当時のドイツの憲法を手本に作られた。
今でいうドイツにあたるプロイセン(当時)の憲法は、君主に強い権利を与えていた。そのプロイセン憲法を参考に、条文の根拠を日本の歴史に求めながら大日本帝国憲法は作られた。
明治憲法で定められた日本の主権者は天皇であり、日本国民ではない。その一方で天皇にも憲法を遵守するべきという立憲制のような義務を明治憲法では定めてある。これは五箇条の御誓文に基づくものである。
そして明治憲法は、主権者である天皇が、国民に対して憲法を授けるという'''欽定憲法'''(きんてい けんぽう)というものであった。
(欽定憲法とは、君主主権の憲法のこと。いっぽう、民衆が制定し、民衆の主権の憲法を民定憲法(みんてい けんぽう)という。)
また、人民の基本的人権については、「法律ノ範囲内」とするというものであり「法律の留保」という条件が付いていた。今日の日本国憲法での、人権基本的人権は永久・不可侵という権利という考え方とは、違っている。
また、政治による軍隊の指揮権に関しては、明治憲法では天皇が軍を統治するというものであり、議会による軍の統治ではなかった。
このように明治憲法では、軍隊の指揮権が議会から独立しているので、これを統帥権の独立といい、統帥権は天皇大権(てんのう たいけん)とされた。
しかし日清戦争や日露戦争では、実質的には、議会と関わりの深い内閣の総理大臣が最終的には軍を指揮していたので、実態は明治憲法の名目とは異なる。しかし、満州事変の以降、軍部は、議会の国際協調路線に反発し、議会が軍部を抑えようとすると、軍部は天皇大権である統帥権の独立を根拠にして、議会による制御は統帥権を侵害するものだと主張して、軍部は議会に反発し、軍部は議会に従わずに暴走していった。
内閣については、名目上は内閣は天皇の補助にすぎなかった。このことを、内閣は天皇の「輔弼」(ほひつ)である,などという。
司法についても、名目上は、天皇を補助する機関にすぎない。議会についても、名目上は、天皇を補助する機関にすぎない。
このように、明治憲法では、天皇が、司法・立法・行政をすべて統治権(とうちけん)を持っていた。
もちろん、実際に裁判所で司法の実務を行ったりするのは裁判官であるし、役所などでの行政の実務を行うのは、その役所の公務員などである。
== 日本国憲法 ==
=== 日本国憲法の制定時の経緯 ===
第二次世界大戦の終戦後に日本を占領したアメリカ軍の'''連合国軍総司令部'''('''GHQ'''、General HeadQuarters)の司令官マッカーサーが、占領政策のための大日本帝国憲法の改憲案として憲法草案要綱を元に作られた'''マッカーサー草案'''が、現代の日本国憲法の、もとになっている。このマッカーサー草案を元に、日本国政府が新憲法の草案(そうあん)を作った。
日本国憲法は、大日本帝国憲法の改憲として帝国議会に提出され、帝国議会で草案は可決され、こうして日本国憲法は制定され1946年に公布された。
日本国憲法は、「'''国民主権'''」「'''平和主義'''」「'''基本的人権の尊重'''」を3大原則とする。
[[カテゴリ:憲法]]
14hyd6dqu20921q4e9rbxzy3n83evxe
Rust
0
32885
264527
264467
2024-11-30T00:10:47Z
Ef3
694
/*演算子*/ Rustでは、演算子を利用して変数やリテラルの値を操作することができます。これらは算術計算、比較、論理操作、ビット操作など、さまざまな目的に応じて使用されます。Rustの演算子は、型安全性と明確性を重視して設計されており、他のプログラミング言語と似た点も多いですが、いくつかの独自の特性も持っています。
264527
wikitext
text/x-wiki
{{Pathnav|メインページ|工学|情報技術|プログラミング}}
Rustは、安全性とパフォーマンスを兼ね備えたモダンなシステムプログラミング言語です。本書では、Rustの基礎から応用までを網羅し、実践的なプログラミングスキルの習得を目指します。Rustは初めてのプログラミング言語としても、既存のスキルをさらに強化するためにも最適です。その特徴である所有権システムや並行性モデルについても詳しく解説し、堅牢で効率的なプログラムを構築するための知識を提供します。一緒にRustの世界を探求し、その魅力を存分に体験していきましょう。
== はじめに ==
Rust(ラスト)は、高性能かつ安全な並行処理を実現するために設計されたマルチパラダイム汎用プログラミング言語です<ref>{{cite web
|url=https://graydon2.dreamwidth.org/247406.html
|title=Rust is mostly safety
|last=Hoare
|first=Graydon
|date=2016-12-28
|website=Graydon2
|publisher=Dreamwidth Studios
|access-date=2021-12-03
|archive-date=2019-05-02
|archive-url=https://web.archive.org/web/20190502181357/https://graydon2.dreamwidth.org/247406.html
|url-status=live }}</ref>。
Rustの構文はC++に似ており、ボローチェッカーを利用して参照の検証を行い、メモリ安全性を保証しています。ボローチェッカーはRustコンパイラによって提供される静的解析ツールで、所有権システムに基づいてコード内の不正な借用(ボロー)を検出します。これにより、メモリ管理においてガベージコレクターを使わずに安全性を実現し、場合によってはリファレンスカウントによるメモリ管理も行えます。
また、Rustはシステムプログラミング言語でありながら、関数型プログラミングの要素も取り入れ、低レベルのメモリ管理を可能にしています。これにより、高度な制御が求められるアプリケーション開発に適しており、実行時エラーの発生を抑えながら、安全で信頼性の高いコードを作成することができます。Rustを通じて、効率的かつ堅牢なプログラム構築のための新しい可能性を探求していきましょう。
{{コラム|width=100%|セルフホスティング|2=セルフホスティングとは、ソフトウェアが自分自身をコンパイルできる状態を指し、他のコンパイラを用意することなく、そのソフトウェア自体で再構築が可能であることを意味します。コンパイラにおけるセルフホスティングは、最初に構築されたコンパイラを用いて同じ言語で書かれたソースコードを再コンパイルし、新しいコンパイラを生成する手法です。このプロセスを繰り返すことで、コンパイラを改良したバージョンに更新していけるのが特徴です。
セルフホスティングは、信頼性と安定性を高める手段でもあり、自分自身をコンパイルできる状態であることがソフトウェアの品質向上に寄与します。また、コンパイラの開発者にとっても、その言語やコンパイラの動作原理や構造について理解を深める機会となり、より効率的でパフォーマンスの良いソフトウェアの開発につながります。Rustもセルフホスティングを実現しており、こうした継続的な改善を通じて、安全性とパフォーマンスの向上を目指しています。
}}
== クイックツアー ==
Rustはメモリ安全性、並行性、パフォーマンスの向上に焦点を当てたモダンなプログラミング言語です。以下のRustのクイックツアーで、基本的な概念とコード例を紹介します。
# 基本構文:
#: Rustプログラムは<code>main</code>関数から始まります。<code>println!</code> マクロを使って標準出力に文字列を出力できます。
#;[https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=65f8e3b00b49ca5c691cd61bfc32d1b7 hello.rs]:<syntaxhighlight lang=rust copy>
fn main() {
println!("Hello, world!");
}
</syntaxhighlight>
#;実行結果:<syntaxhighlight lang=text>
Hello, world!
</syntaxhighlight>
#: [https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=65f8e3b00b49ca5c691cd61bfc32d1b7 hello.rs]は、[https://play.rust-lang.org/ Playground]に作った、このプログラムへのリンクになっています。
# データ型:
#: Rustには整数、浮動小数点数、真偽値などの基本データ型があります。
#:<syntaxhighlight lang=rust copy>
let age: i32 = 25;
let salary: f64 = 50000.50;
let is_rust_fun: bool = true;
let message: &str = "Hello, Rust!";
</syntaxhighlight>
# 制御構造:
#: <code>if</code>、<code>else if</code>、<code>else</code> 文で条件分岐ができます。
#: <code>while</code> ループや <code>for</code> ループで繰り返し処理ができます。
#:<syntaxhighlight lang=rust copy>
let num = 10;
if num > 0 {
println!("Positive");
} else if num < 0 {
println!("Negative");
} else {
println!("Zero");
}
for i in 0..5 {
println!("Iteration {}", i);
}
</syntaxhighlight>
# 関数:
#: 関数は <code>fn</code> キーワードを使って宣言します。
#:<syntaxhighlight lang=rust copy>
fn add(a: i32, b: i32) -> i32 {
a + b
}
fn main() {
let result = add(5, 3);
println!("Sum: {}", result);
}
</syntaxhighlight>
# 所有権システム:
#: Rustは所有権ベースのメモリ管理を採用しており、値の所有権が明確に定義されています。
#:<syntaxhighlight lang=rust copy>
fn main() {
let s1 = String::from("Hello");
let s2 = s1; // s1の所有権がs2に移動する(所有権の転送)
// println!("{}", s1); // エラー!s1はもう有効ではない
println!("{}", s2); // 正常に動作
}
</syntaxhighlight>
# 構造体とメソッド:
#: 構造体はデータをまとめるためのカスタム型で、メソッドを持つことができます。
#:<syntaxhighlight lang=rust copy>
struct Car {
model: String,
year: u32,
}
impl Car {
fn display_info(&self) {
println!("Model: {}, Year: {}", self.model, self.year);
}
}
fn main() {
let my_car = Car {
model: String::from("TOYOTA 86"),
year: 2022,
};
my_car.display_info();
}
</syntaxhighlight>
ここでは、Rustの基本的な構文とコンセプトを簡単に紹介しました。
== rustcのバージョン確認 ==
やや力技ですが、Rustのコンパイラ rustc のバージョンをコードから確認できます。
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=db39d32bceac679dd79591501075d7f6 version.rs]:<syntaxhighlight lang=rust copy>
fn main() {
let version = std::process::Command::new("rustc")
.arg("--version")
.output()
.expect("Failed to get Rust version");
if version.status.success() {
let stdout = String::from_utf8_lossy(&version.stdout);
println!("Rust version: {}", stdout);
} else {
eprintln!("Failed to retrieve Rust version information");
}
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
Rust version: rustc 1.84.0-nightly (1e4f10ba6 2024-10-29)
</syntaxhighlight>
このコードは、Rustのプログラム内で<code>rustc --version</code>コマンドを実行し、その結果(Rustコンパイラのバージョン情報)を取得しています。
# <code>std::process::Command::new("rustc")</code>: <code>Command</code>構造体を使って新しいコマンドを作成しています。ここでは<code>rustc</code>というコマンドを実行するよう指定しています。
# <code>.arg("--version")</code>: <code>rustc</code>コマンドに<code>--version</code>引数を渡しています。これにより、Rustコンパイラのバージョン情報を取得するよう指示しています。
# <code>.output()</code>: <code>Command</code>を実行して、その結果を取得します。ここでは<code>--version</code>を指定した<code>rustc</code>コマンドを実行し、その出力を取得しています。
# <code>.expect("Failed to get Rust version")</code>: コマンドの実行が失敗した場合にエラーメッセージを表示します。
# <code>if version.status.success() { ... } else { ... }</code>: 実行結果の<code>status</code>をチェックして、コマンドが正常に終了したかどうかを確認します。もし成功していた場合は、コマンドの出力結果(Rustコンパイラのバージョン情報)を取得し、それを標準出力に表示します。もし失敗していた場合は、エラーメッセージを標準エラー出力に表示します。
このコードは、Rustのプログラム内で外部コマンドを実行してその出力を取得する方法を示しています。具体的には、Rustコンパイラのバージョン情報を取得してそれを表示する例です。
== コメント ==
Rustのコメントには、[[C言語]]/[[C++]]と同じく一行コメントと範囲コメントがあります。
;一行コメント: <code>//</code>から行末までがコメントと見なされます。
;範囲コメント
: <code>/*</code>から<code>*/</code>までがコメントと見なされます。
: ネストは許されません。
;コメントの例:<syntaxhighlight lang=rust copy>
/*
* プログラムのエントリーポイントは、main 関数です。
* 関数定義は fn から始まります。
*/
fn main() {
println!("Hello, world!"); // println! は関数ではなくマクロで、マクロは識別子の末尾に ! が付きます。
}
</syntaxhighlight>
== 変数と型 ==
Rustでは、変数を宣言する際にはデフォルトでimmutable(不変)です。変更可能な変数を宣言するには、<code>mut</code> キーワードを使用します。変数の型はコンパイラによって推論されることが一般的ですが、型を明示的に指定することもできます。
例えば、変数の宣言と型指定は以下のようになります:
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=8cddaf15c8b53acb5e0a2013a5cb8cc2 decl.rs]:<syntaxhighlight lang=rust copy>
fn main() {
// 型推論による変数の宣言
let x = 5; // 整数型 i32 として推論される
let y = 3.14; // 浮動小数点型 f64 として推論される
println!("x = {x}, y = {y}");
// 型を明示的に指定する
let z: i64 = 100; // 64ビット整数型 i64
println!("z = {z}");
}
</syntaxhighlight>
Rustの基本的なデータ型には以下があります:
* 整数型 (<code>i8</code>, <code>i16</code>, <code>i32</code>, <code>i64</code>, <code>i128</code>, <code>u8</code>, <code>u16</code>, <code>u32</code>, <code>u64</code>, <code>u128</code>など)
* 浮動小数点型 (<code>f32</code>, <code>f64</code>)
* 論理値型 (<code>bool</code>)
* 文字型 (<code>char</code>)
* ポインタ型
* タプル型
* 配列型
* 列挙型
* 構造体型
* 文字列型 (<code>&str</code>, <code>String</code>)
Rustは静的型付け言語であり、変数の型はコンパイル時に確定されます。型の安全性に対する厳格なチェックを行うため、コンパイル時に型の整合性が確認されます。これにより、メモリの安全性やスレッドセーフなコードを書く際の支援が期待できます。
=== 変数とミュータブル・イミュータブル ===
Rustでは、変数を宣言するにはキーワード '''let'''を使います。
ディフォルトでは[[#イミュータブル|イミュータブル]](''Immutable'';宣言後には代入不能)な変数が宣言されます。
[[#ミュータブル|ミュータブル]](''Mutable'';宣言後に代入可能)な変数を宣言するには、追加のキーワード '''mut''' を使います。
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=9032ae8999c6cb3bf5412988320e50d7 hello-variables.rs]:<syntaxhighlight lang=rust highlight=2 line copy>
fn main() {
let hello : &str = "Hello, world!";
println!("{}", hello);
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
Hello, world!
</syntaxhighlight>
:2行目の<syntaxhighlight lang=rust inline>let hello : &str = "Hello, world!";</syntaxhighlight>が変数宣言です<ref>文字リテラルであることを強調するなら<syntaxhighlight lang=rust inline>let hello : &'static str = "Hello, world!";</syntaxhighlight>とすべきだったかもしれません。</ref>。
::&str(文字列のスライスのリファレンス)を型とする変数 <var>hello</var> を宣言し、"Hello, world!"で初期化しています。
::Rustには強力な[[#型推論|型推論]]があり多くの場合不要ですが、<code>let 変数名 : 型名</code>の書式で型を伴い変数宣言することも出来ます。
mut をつけない場合には変数に「代入不能」と聞くと、C言語などを知っている人は「定数」を思い浮かべるかもしれませが、
Rustにおいて「定数」は, const 宣言された定数や, static 宣言されかつ mut で修飾されていない変数が相当します。
==== 型推論 ====
Rust では、変数宣言が初期値を伴っていた場合、変数の型を省略することができ、初期値の型が変数の型になります。
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=ac2b2d100ee8ea175178ebe9b1e26c61 hello-type-inference.rs]:<syntaxhighlight lang=rust highlight=2 line copy>
fn main() {
let hello = "Hello, world!";
println!("{hello}");
}
</syntaxhighlight>
;実行結果:上に同じ
==== イミュータブル ====
Rust では、値が一度変数に let で束縛されると変更できません。これをイミュータブルと言います。
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=d13ccba2d1b5efef4ca929012eece549 hello-immutable.rs]:<syntaxhighlight lang=rust highlight='2,4' line copy>
fn main() {
let hello : &str = "Hello, world!";
println!("{hello}");
hello = "Hello, rust!";
println!("{hello}");
}
</syntaxhighlight>
;コンパイル結果:<syntaxhighlight lang=text>
error[E0384]: cannot assign twice to immutable variable `hello`
--> src/main.rs:4:5
|
2 | let hello : &str = "Hello, world!";
| -----
| |
| first assignment to `hello`
| help: consider making this binding mutable: `mut hello`
3 | println!("{hello}");
4 | hello = "Hello, rust!";
| ^^^^^^^^^^^^^^^^^^^^^^ cannot assign twice to immutable variable
For more information about this error, try `rustc --explain E0384`.
error: could not compile `playground` (bin "playground") due to 1 previous error
</syntaxhighlight>
:イミュータブルな変数には、代入できないというコンパイルエラーです。
==== ミュータブル ====
代入可能、すなわちミュータブルにするためには、変数宣言にあたり '''let''' に続けて '''mut''' をつけます。
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=6bbb520249ebc44f7e341988dfad92a3 hello-mutable.rs]:<syntaxhighlight lang=rust highlight=2 line copy>
fn main() {
let mut hello : &str = "Hello, world!";
println!("{hello}");
hello = "Hello, rust!";
println!("{hello}");
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
Hello, world!
Hello, rust!
</syntaxhighlight>
==== 同じ変数名での宣言 ====
同一スコープで同じ変数名での宣言は可能です。
同じ型である必要はありません。ミュータブルであるかイミュータブルであるかも問いません。
'''同じ変数名での宣言によって、それまで変数に束縛されていた値への参照がなくなります。'''
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=0604ce8c08449bba14b64f80c405815d 同じ変数名での宣言]:<syntaxhighlight lang=rust highlight='2,4' line copy>
fn main() {
let hello : &str = "Hello, world!";
println!("{}", hello);
let hello = 154649;
println!("{}", hello);
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
Hello, world!
154649
</syntaxhighlight>
==== 定数 ====
Rustには2種類の定数があり、どちらもグローバルスコープを含む任意のスコープで宣言することができます。また、どちらも明示的な型を持っている必要があります。
* const: 不変の値
* static: 静的寿命を持つミュータブルな値 静的寿命は推論されるので、指定する必要はありません。
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=901ef30e5d150a91c9fed78d63a4971d 2種類の定数]:<syntaxhighlight lang=rust highlight='2,3' line copy>
const HELLO : &str = "Hello, world!";
static LANGUAGE: &str = "Rust";
fn main() {
println!("{HELLO}");
println!("{LANGUAGE}");
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
Hello, world!
Rust
</syntaxhighlight>
コードを書き換えてconst宣言や(ミュータブルな)static宣言された変数に代入をしようとすると、エラーになります。
==== パターン ====
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=9e938e1309c6ba12f760a6c6f0c9d210 pattern.rs]:<syntaxhighlight lang=rust line highlight=5 copy>
fn main() {
let (mut x, mut y) = (5, 29);
println!("x={x} y={y}");
(x, y) = (y, x);
println!("x={x} y={y}");
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
x=5 y=29
x=29 y=5
</syntaxhighlight>
このコードは、<code>x</code> と <code>y</code> の値を交換するRustの機能を示しています。
* 最初の行では、<code>x</code> に 5 を、<code>y</code> に 29 を代入しています。
* 次の行では、<code>println!</code> マクロでは、交換後の <code>x</code> と <code>y</code> の値を表示しています。
* 次の行では、<code>(x, y) = (y, x);</code> という操作を行っています。これは、タプルを使って複数の変数に同時に値を代入しています。この場合、<code>(y, x)</code> というタプルの中身を <code>(x, y)</code> に順番に代入しています。これにより、<code>x</code> の値に <code>y</code> の値が入り、<code>y</code> の値に <code>x</code> の値が入ります。これによって <code>x</code> と <code>y</code> の値が交換されます。
*最後の <code>println!</code> マクロでは、交換後の <code>x</code> と <code>y</code> の値を表示しています。
このコードは、Rustのタプルを使った多値代入の機能を示しています。
=== データ型 ===
Restには豊富なデータ型(''Data Types'')があり、それらを組み合わせて新しい型を作ることができます<ref>{{Cite web
|url=https://doc.rust-lang.org/book/ch03-02-data-types.html
|title=Data Types - The Rust Programming Language
|accessdate=2021/12/08
}}</ref>。
==== スカラー型(''Scalar Types'') ====
スカラー型は単一の値を表します。Rustには、整数、浮動小数点数、論理値、文字という4つの主要なスカラ型があります。
===== 整数型(''Integer Types'') =====
Rustの整数型は、符号の有無とビット幅から12種類のバリエーションがあります。
:{| class=wikitable
|+ Rustの整数型
!型名!!説明
|-
!i8
|符号付き8ビット整数
|-
!u8
|符号なし8ビット整数
|-
!i16
|符号付き16ビット整数
|-
!u16
|符号なし16ビット整数
|-
!i32
|符号付き32ビット整数
|-
!u32
|符号なし32ビット整数
|-
!i64
|符号付き64ビット整数
|-
!u64
|符号なし64ビット整数
|-
!i128
|符号付き128ビット整数
|-
!u128
|符号なし128ビット整数
|-
!isize
|符号付きでポインタと同じサイズの整数
|-
!usize
|符号なしでポインタと同じサイズの整数
|}
:isizeとusizeのビット幅はプロセッサーのアーキテクチャーによって定義され、32ビットプロセッサーならば32、64ビットプロセッサーならば64です。
===== 整数リテラル(''Integer literals'') =====
リテラル(''Literals'')とは、プログラミングのソースコードで使用される、数値や文字列などのデータを直接表現したものです。
:{| class=wikitable style="float:left"
|+ 様々な整数リテラル
!基数!!表現
|-
!10
|19_800
|-
!16
|0xbadbeef
|-
!8
|0o777
|-
!2
|0b101_111_011
|-
!バイト(u8のみ)
|b'Q'
|}
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=9c41862df3833ee7bd16169631d74de7 例]:<syntaxhighlight lang=rust style="float:left;width:24em; margin: 1em">
fn main() {
println!("{:?}", 19_800);
println!("{:x}", 0xbadbeef);
println!("{:o}", 0o777);
println!("{:b}", 0b101_111_011);
println!("{}", b'Q');
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text style="float:left;width:12em; margin: 1em">
19800
badbeef
777
101111011
81
</syntaxhighlight>
:<br style="clear:both">
:数値リテラルには、123u8 の様に型名をタイプサーフィックス(''type suffix'')として補うことで、ビット幅を明記できます(オプショナル)。
::指定されない場合は(バイト以外は)i32が仮定されます(isizeではありません)。
:数値リテラルには、読みやすさのため 9_281_636 のように、アンダースコア _ を補うことができます(オプショナル)。
なお、<code>{:x}</code>の<code>{x}</code>部分は[[#プレースホルダー|プレースホルダー]]の[[#フォーマッティング・トレイツ(Formatting traits)|ファーマッティング・トレイツ]]です。「x」なら16進数、oなら8進数、bなら2進数で出力します。
===== 浮動小数点数型(''Floating-Point Types'') =====
Rustには、浮動小数点数を表現するための2つの主要な型があります。それぞれの型は、IEEE-754規格に従っています。
# <code>f32</code>: 32ビットの単精度浮動小数点数型です。精度は約6桁です。
# <code>f64</code>: 64ビットの倍精度浮動小数点数型です。精度は約15桁です。
Rustでは、浮動小数点数リテラルを書く場合、デフォルトで <code>f64</code> 型になります。例えば、<code>3.14</code> という浮動小数点数リテラルは、<code>f64</code> 型の数値になります。
以下は、<code>f32</code> 型と <code>f64</code> 型の浮動小数点数の使用例です。
:<syntaxhighlight lang=rust copy>
fn main() {
// デフォルトでは f64 型になる浮動小数点数
let my_float1 = 3.14; // f64 型
// サイズを明示して f32 型にする
let my_float2: f32 = 2.718; // f32 型
// 浮動小数点数同士の計算
let sum = my_float1 + f64::from(my_float2); // f64 型にキャストして計算
println!("Sum: {}", sum); // f64 型の結果が出力される
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=rust copy>
Sum: 5.857999935150147
</syntaxhighlight>
浮動小数点数は、数値計算や科学的な計算など、精度が求められる場面で使用されます。しかし、浮動小数点数の性質や精度による注意が必要な場面もありますので、注意深く扱う必要があります。
===== 論理値型(''The Boolean Type'') =====
Rustにおける論理値型の型名は <code>bool</code> で、真の値は <code>true</code>、偽の値は <code>false</code> です。この型は非常に基本的で、条件分岐やブール演算などで使用されます。
以下は <code>bool</code> 型の使用例です。
:<syntaxhighlight lang=rust copy>
fn main() {
let is_rust_cool = true; // 真の値を持つ変数
let is_java_cool = false; // 偽の値を持つ変数
if is_rust_cool {
println!("Rust is cool!"); // 条件が true の場合に実行される
} else {
println!("Rust is not cool."); // 条件が false の場合に実行される
}
// 論理演算
let result = is_rust_cool && is_java_cool; // 論理積 (AND) の例
println!("Result of logical AND: {}", result); // false が出力される
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
Rust is cool!
Result of logical AND: false
</syntaxhighlight>
<code>bool</code> 型は条件式の評価や論理演算に広く使用され、プログラムの流れを制御するための基本的な手段として重要な役割を果たします。
===== 文字型(''The Character Type'') =====
Rustの文字型 <code>char</code> はUnicodeの単一の文字を表し、32ビットで符号化されます。Unicodeのサロゲートペアを含む広範な範囲の文字を表現できます。
以下のコードは、<code>char</code>型を使用してUnicode文字やサロゲートペアを扱う例です。
:<syntaxhighlight lang=rust copy>
fn main() {
// 単一のUnicode文字の表現
let unicode_char = '😊'; // 笑顔の絵文字 (U+1F60A)
println!("Unicode char: {}", unicode_char);
// サロゲートペアの表現
let surrogate_pair = '\u{1F601}'; // 涙の絵文字 (U+1F601)
println!("Surrogate pair: {}", surrogate_pair);
// char型のサイズを取得
println!("Size of char: {} bytes", std::mem::size_of::<char>());
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
Unicode char: 😊
Surrogate pair: 😁
Size of char: 4 bytes
</syntaxhighlight>
ここでは、<code>'😊'</code> という絵文字や <code>\u{1F601}</code> というサロゲートペアを <code>char</code>型として表現しています。絵文字やサロゲートペアも正しく表示されることを確認できます。また、<code>std::mem::size_of::<char>()</code> を使って <code>char</code>型のサイズを表示しています。
===== 文字列型(''The String Type'') =====
Rustには2つの主要な文字列型があります。
# <code>&str</code>型 (文字列スライス):
#* メモリ内のデータへの不変の参照を表します。
#* UTF-8でエンコードされた文字列を参照します。
#* 文字列リテラルや他のデータ構造の一部として使用されます。
#:<syntaxhighlight lang=rust copy>
let string_slice: &str = "Hello, Rust!"; // 文字列リテラルから作成された文字列スライス
</syntaxhighlight>
# <code>String</code>型:
#* ヒープ上に確保された可変の文字列データを持ちます。
#* 動的に変更可能で、文字列の追加や削除、変更が可能です。
#:<syntaxhighlight lang=rust copy>
let mut string_object = String::from("Hello"); // String型のインスタンスを生成
string_object.push_str(", Rust!"); // 文字列を追加
</syntaxhighlight>
これらの型は互いに相互変換できます。例えば、<code>&str</code>から<code>String</code>への変換は<code>to_string()</code>メソッドを使用できます。
:<syntaxhighlight lang=rust copy>
let my_string: String = "Hello".to_string(); // &strからStringへの変換
</syntaxhighlight>
また、<code>String</code>から<code>&str</code>への変換は、<code>&</code>演算子を使用して参照を取得します。
:<syntaxhighlight lang=rust copy>
let my_string: String = String::from("Hello");
let string_ref: &str = &my_string; // Stringから&strへの変換
</syntaxhighlight>
文字列操作に関しては、<code>String</code>型が動的に変更可能で柔軟性があり、<code>&str</code>型は主に静的な文字列の参照として使用されます。
;様々な文字列リテラル
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=9c41862df3833ee7bd16169631d74de7 string.rs]:<syntaxhighlight lang=rust copy>
fn main() {
println!("{:?}", "hello"); // エスケープされた文字列
println!("{:?}", r#"hello"#); // エスケープされないraw文字列
println!("{:?}", b"hello"); // エスケープされたバイト文字列
println!("{:?}", br#"hello"#); // エスケープされないrawバイト文字列
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
"hello"
"hello"
[104, 101, 108, 108, 111]
[104, 101, 108, 108, 111]
</syntaxhighlight>
* <code>"{:?}"</code> はデバッグ用のフォーマット指定子で、デバッグ表示用の形式で出力します。
* <code>r#"..."#</code> はエスケープされない raw 文字列リテラルで、内部のエスケープが無視されます。
* <code>b"..."</code> はバイト文字列リテラルで、ASCII文字のバイト値の配列を示します。
* <code>br#"..."#</code> はエスケープされない raw バイト文字列リテラルです。
これらのリテラルは、異なる用途で利用されることがあり、それぞれの特性や振る舞いが異なります。
===== ユニット型(''The Unit Type'') =====
Rustにおけるユニット型は<code>()</code>で表されます。ユニット型は特別な型であり、単一の値 <code>()</code> だけから成り立ちます。主に2つの用途があります:
# 関数の戻り値としての利用: 副作用のない関数や手続きにおいて、何も返す必要がない場合にユニット型 <code>()</code> が使用されます。
#:<syntaxhighlight lang=rust copy>
fn do_something() {
// 何らかの処理
}
fn main() {
let result = do_something(); // 戻り値は () になる
println!("result = {:?}", result);
}
</syntaxhighlight>
# 構造体のフィールドとしての利用: 構造体のフィールドとしてユニット型を持つことで、その構造体のインスタンスが存在することを示す場合に使用されます。
#:<syntaxhighlight lang=rust copy>
#[derive(Debug)]
struct MarkerUnit;
fn main() {
let marker = MarkerUnit; // ユニット型を持つ構造体のインスタンス化
println!("marker = {:?}", marker);
}
</syntaxhighlight>
ユニット型は一見すると何も持たない型ですが、プログラムの構造を表現するために重要な役割を果たしています。特に関数の戻り値として使用されることが多いです。
==== 複合型(''Compound Types'') ====
複合型(''Compound Types'')は、複数の値を1つの型にまとめることができます。
Rustにはタプルとアレイという2つのプリミティブな複合型があります。
===== タプル型(''The Tuple Type'') =====
タプル(''The Tuple'')は、さまざまな型の値を1つの複合型にまとめる一般的な方法です。
タプルの長さは固定されており、一度宣言すると大きくしたり小さくしたりすることはできません。
===== 配列型(''The Array Type'') =====
複数の値の集まりを持つもう一つの方法として、配列(''The Array'')があります。
タプルとは異なり、配列の各要素は同じ型でなければなりません。
Rustの配列は、タプルと同じく長さが固定されています。
== プレースホルダー ==
Rustの<code>println!</code>マクロなどの文字列表示マクロでは、文字列中の<code>{}</code>の位置に指定された書式に基づいて値が展開されます<ref>[https://doc.rust-lang.org/std/fmt/ Module std::fmt]</ref>。
=== フォーマッティング・トレイツ (Formatting traits) ===
Rustにおけるフォーマッティング・トレイツは、データを指定されたフォーマットで整形するためのトレイト(trait)です。例えば、空のプレースホルダー<code>{}</code>を指定すれば、その型に適した自然なフォーマット(例:100ならば "100")で表示できます。これには、[https://doc.rust-lang.org/std/fmt/trait.Display.html fmt::Display]トレイトが使用されます。
一方、基数を変更して16進数、8進数、2進数などの異なる表現で表示したい場合には、フォーマッティング・トレイツを活用します<ref>[https://doc.rust-lang.org/std/fmt/#formatting-traits Formatting traits]</ref>。
;[https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=3e0f75ec3db6eaa2f1db03cefefb5b44 Formatting traits]:<syntaxhighlight lang=rust copy>
fn main() {
println!("{:?}", 100); // Debug
println!("{:x}", 100); // LowerHex
println!("{:o}", 100); // Octal
println!("{:b}", 100); // Binary
println!("{}", 100); // Display
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
100
64
144
1100100
100
</syntaxhighlight>
フォーマッティング・トレイツは、Rustプログラミング言語でデータをフォーマットするためのメカニズムを提供します。これらのトレイツは、<code>std::fmt</code>モジュールに定義されており、さまざまなフォーマット方法を提供します。代表的なものは以下の通りです:
* '''Display''': <code>std::fmt::Display</code>トレイトは、<code>{}</code>を使用し、一般的に人間が読みやすい形式でデータを表示します。<code>println!</code>や<code>format!</code>マクロなどで使用されます。
* '''Debug''': <code>std::fmt::Debug</code>トレイトは、<code>{:?}</code>を使ってデバッグ目的で表示を行います。通常、開発中にデータの内容を調べるために使用されます。
* '''Binary''': <code>std::fmt::Binary</code>トレイトは、<code>{:#b}</code>でバイナリ形式での表示を提供します。
* '''Octal''': <code>std::fmt::Octal</code>トレイトは、<code>{:#o}</code>で8進数形式の表示を提供します。
* '''LowerHex / UpperHex''': <code>std::fmt::LowerHex</code>および<code>std::fmt::UpperHex</code>トレイトは、それぞれ<code>{:#x}</code>および<code>{:#X}</code>で16進数の小文字・大文字形式で表示します。
これらのトレイツは、カスタム型のデータを整形するために実装することができ、例えば<code>Display</code>トレイトを実装することで、カスタム型のデータを<code>{}</code>形式で表示できるようになります。
以下は、構造体<code>Person</code>に<code>Display</code>トレイトを実装し、<code>{}</code>を使用してカスタム型を表示する例です。
;[https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=1dc6f34658b858b48dad035ca7ef7117 カスタムDisplayトレイトの例]
:<syntaxhighlight lang=rust copy>
use std::fmt;
// 構造体を定義
struct Person {
name: String,
age: u32,
}
// Displayトレイトの実装
impl fmt::Display for Person {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// カスタムフォーマットを定義する
write!(f, "Name: {}, Age: {}", self.name, self.age)
}
}
fn main() {
let person = Person {
name: String::from("Alice"),
age: 30,
};
// {}を使ってPerson型を表示
println!("Person Details: {}", person);
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
Person Details: Name: Alice, Age: 30
</syntaxhighlight>
この例では、<code>Person</code>という構造体を定義し、<code>fmt::Display</code>トレイトを実装しています。<code>Formatter</code>型を使ってカスタムフォーマットを定義し、<code>write!</code>マクロを使って書式設定を行っています。<code>main</code>関数内で、<code>println!</code>マクロを使って<code>Person</code>型のインスタンスを表示し、<code>{}</code>を使用してカスタム型を整形する方法を示しています。
=== 浮動小数点数のフォーマット ===
浮動小数点数の表示には、<code>std::fmt::Float</code> トレイトを使います。以下の書式を使って、浮動小数点数の表現をフォーマットできます。
* '''e''': 指数形式(例:<code>1.23e4</code>)
* '''f''': 固定小数点形式(例:<code>123.456</code>)
* '''g''': 自動的に指数形式または固定小数点形式に切り替える(例:<code>1.23</code>)
* '''p''': 精度の高い16進数形式(例:<code>0x1.9p+1</code>)
例えば、以下のように浮動小数点数を表示することができます:
:<syntaxhighlight lang=rust copy>
let pi = 3.141592;
println!("{:.2}", pi); // 固定小数点形式で小数点以下2桁表示
println!("{:e}", pi); // 指数形式で表示
println!("{:g}", pi); // 自動的に形式を切り替える
println!("{:p}", pi); // 精度の高い16進数形式
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
3.14
3.141592e0
3.14159
0x1.921fb6p+1
</syntaxhighlight>
==== 浮動小数点の精度指定 ====
浮動小数点数の表示において、精度(小数点以下の桁数)を指定するには、書式の中で <code>.</code> の後に桁数を指定します(例:<code>{:.2f}</code>)。デフォルトでは、浮動小数点数の精度は6桁ですが、<code>{:.8e}</code> や <code>{:.12f}</code> で精度を変更することができます。
=== 文字列のフォーマット ===
文字列のフォーマットには、<code>std::fmt::Display</code> トレイトに基づいて、さまざまなオプションがあります。文字列に対しては、特に幅やアライメントを指定することができます。以下のような書式を使用できます:
* '''Width''': フィールドの幅を指定します。例えば、<code>{:10}</code> と書くと、最小で10文字分のスペースが確保され、足りない分は空白で埋められます。
* '''Alignment''': 左寄せ、右寄せ、中央寄せを指定できます。左寄せは <code><</code>、右寄せは <code>></code>、中央寄せは <code>^</code> を使います。
以下に文字列のフォーマット例を示します:
:<syntaxhighlight lang=rust copy>
let s = "Rust";
println!("{:>10}", s); // 右寄せで幅10
println!("{:<10}", s); // 左寄せで幅10
println!("{:^10}", s); // 中央寄せで幅10
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
Rust
Rust
Rust
</syntaxhighlight>
上記の例では、文字列 <code>"Rust"</code> に対して異なるアライメント(右寄せ、左寄せ、中央寄せ)を指定しています。
== 所有権システム ==
Rustの所有権システムは、メモリ管理とリソースの安全な扱いを通じて安全性と並行性を確保する、言語の重要な特徴です。ここでは、Rustの所有権、借用、参照について、コードを交えて解説します。
=== 所有概念 ===
Rustの所有概念は、変数がリソースの所有権を持ち、スコープを抜ける際にそのリソースを自動的に解放する仕組みです。この仕組みは、メモリ安全性を高めるための中心的な考え方です。
=== コピーとムーブ ===
Rustでは、変数が参照しているデータがスタック上かヒープ上かによって、コピーやムーブの挙動が異なります。スタック上のプリミティブ型はコピーされますが、ヒープ上のデータはムーブされ、所有権が移動します。
; コピーされるデータ
:<syntaxhighlight lang=rust line copy>
fn main() {
let x = 5;
let y = x; // xの値がコピーされる
println!("x: {}, y: {}", x, y); // 両方の変数が利用可能
}
</syntaxhighlight>
この例では、整数型<code>x</code>の値がコピーされるため、<code>x</code>と<code>y</code>の両方が使用できます。
; ムーブされるデータ
:<syntaxhighlight lang=rust line copy>
fn main() {
let s1 = String::from("Hello");
let s2 = s1; // s1の所有権がs2に移動
// println!("{}", s1); // コンパイルエラー:s1はもう使用できない
println!("{}", s2); // 正常に出力される
}
</syntaxhighlight>
ここでは、<code>s1</code>の所有権が<code>s2</code>に移動するため、<code>s1</code>は利用できなくなります。
次に、コピーとムーブの違いを簡単に表にまとめます。
:{| class=wikitable
|+ コピーとムーブの違い
! データ種別!! コピー !! ムーブ
|-
! プリミティブ型(整数型、bool型など)
| コピーされる
| コピーされる
|-
! ヒープ上のデータ(String型、Vec型など)
| されない
| 所有権が移動する
|}
=== 借用と参照 ===
所有権を移動せずにデータを利用する方法として、借用(参照)があります。
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=91624181f640bfff93914d318547bfae 借用と参照の例]
:<syntaxhighlight lang=rust line copy>
fn main() {
let s1 = String::from("Hello");
// s1を借用する(イミュータブルな参照)
let len = calculate_length(&s1);
println!("The length of '{}' is {}.", s1, len); // 正常に出力される
}
fn calculate_length(s: &String) -> usize {
s.len()
}
</syntaxhighlight>
この例では、<code>calculate_length</code>関数が<code>&String</code>型のイミュータブルな参照を受け取っているため、<code>s1</code>の所有権を渡すことなく値を参照できます。
=== ミュータブルな参照 ===
可変の値を変更するには、ミュータブルな参照を使います。
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=1ef888bfe58461858b4cefe84d3b6fae ミュータブルな参照の例]
:<syntaxhighlight lang=rust line copy>
fn main() {
let mut s = String::from("Hello");
change_string(&mut s);
println!("{}", s); // "Hello, goodbye"が出力される
}
fn change_string(s: &mut String) {
s.push_str(", goodbye");
}
</syntaxhighlight>
<code>&mut String</code>型のミュータブルな参照を利用して<code>change_string</code>関数で文字列を変更しています。
=== 関数のパラメータにした変数は関数にムーブされる ===
Rustでは、関数の引数として変数を渡すと、所有権が関数にムーブされる場合があります。例で見てみましょう。
:<syntaxhighlight lang=rust copy>
fn main() {
let original = String::from("Hello, Rust!"); // String型の変数originalを作成
let moved = move_ownership(original); // originalがmove_ownership関数にムーブ
// println!("original: {}", original); // コンパイルエラー
println!("moved: {}", moved); // 正常に動作
}
fn move_ownership(input: String) -> String {
input
}
</syntaxhighlight>
この例では、<code>original</code>が関数<code>move_ownership</code>にムーブされ、<code>original</code>は使用できなくなります。
=== クローン ===
所有権を共有したい場合、<code>clone</code>メソッドでデータの複製が可能です。
:<syntaxhighlight lang=rust copy>
fn main() {
let data1 = vec![1, 2, 3];
let data2 = data1.clone(); // データの複製
println!("{:?}", data1); // data1は所有権を保持している
println!("{:?}", data2); // data2はクローンされたデータを持つ
}
</syntaxhighlight>
この例では、<code>data1</code> の <code>clone</code> メソッドを呼び出すことで、ヒープ上にあるデータの複製を作成し、<code>data2</code> に格納します。このとき、<code>data1</code> と <code>data2</code> はそれぞれ独立した所有権を持つため、どちらかを変更してももう一方には影響しません。
クローンを作成することで、所有権が必要な場合でも、元のデータをそのまま保持しながらコピーを生成できるため、データの安全な扱いが可能です。
=== ライフタイム ===
ライフタイムは、参照が有効である期間を示すRustの注釈で、所有権と借用のルールと密接に関係しています。ライフタイムを指定することで、コンパイラがメモリの安全性を保証し、不正なメモリアクセスや参照の無効化を防ぎます。
:<syntaxhighlight lang=rust copy>
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
</syntaxhighlight>
この例では、<code>'a</code> というライフタイムパラメータを使って、<code>x</code> と <code>y</code> の参照が同じライフタイムを共有することをコンパイラに伝えています。これにより、返り値のライフタイムも入力引数と同じ期間で有効であることが保証されます。
ライフタイムは複雑な場面では特に重要となり、Rustの所有権システムがさらに強力にメモリ安全を保証する仕組みとなっています。
=== Rc, Box と Drop ===
==== Rc (Reference Counting) ====
<code>Rc</code>は参照カウントを用いたスマートポインタで、複数の所有者が同じデータを共有できるようにします。主にマルチスレッドを必要としない環境で利用され、<code>clone</code>メソッドでカウントを増加させることで、同じデータを複数の変数が所有できます。
:<syntaxhighlight lang=rust copy>
use std::rc::Rc;
let a = Rc::new(5);
let b = Rc::clone(&a); // aとbが同じデータを共有
</syntaxhighlight>
<code>Rc</code>はスレッドセーフではありませんが、シングルスレッドの環境でデータ共有を簡単に行えるため便利です。
==== Box (Heap Allocation) ====
<code>Box</code>はデータをヒープに格納し、そのポインタをスタック上で保持するスマートポインタです。スタックではなくヒープにデータを配置したい場合に使用します。<code>Box</code>は最も単純なスマートポインタで、シングルオーナーのデータの所有権を提供します。
:<syntaxhighlight lang=rust copy>
let b = Box::new(5);
println!("b = {}", b); // ヒープ上に格納されたデータにアクセス
</syntaxhighlight>
ボックスは特に再帰的なデータ構造の作成やヒープメモリ管理に適しており、ポインタの解放はスコープを外れたときに自動で行われます。
==== Drop (Resource Cleanup) ====
<code>Drop</code>トレイトは、オブジェクトがスコープを外れるときにリソースをクリーンアップするための機構を提供します。カスタムデストラクタを実装する際に使用され、例えばファイルハンドルやネットワークリソースなどのリソース解放に役立ちます。
:<syntaxhighlight lang=rust copy>
struct CustomResource;
impl Drop for CustomResource {
fn drop(&mut self) {
println!("CustomResourceが解放されました");
}
}
let resource = CustomResource;
// スコープを抜けるときにdropメソッドが呼ばれる
</syntaxhighlight>
<code>Drop</code>トレイトにより、Rustは自動的にリソース管理を行い、メモリリークやリソースの取りこぼしを防ぎます。手動で<code>drop</code>を呼び出すことも可能ですが、通常はスコープ終了時に自動で解放されます。
; まとめ
Rustの所有権システムは、メモリ管理とデータ競合の予防において非常に強力な機能です。所有、借用、ムーブ、コピー、クローンといった各機能を活用することで、安全かつ効率的にメモリを管理できるようになります。特に、Rustではコンパイル時にこれらのルールが強制されるため、意図しないバグを未然に防ぐことができます。
Rustの所有権システムを深く理解することは、効率的で安全なコードを書くための第一歩です。<!ーー
RcやBoxやDropもここか?
ーー>
== 制御構造 ==
Rust では、{{code|if}}や{{code|for}}などの制御構造も式です。
=== 分岐 ===
Rust は、[[#if|if]] と [[#match|match]] の2つの分岐構文を持ちます。
==== if ====
ifは、条件式に基づき分岐し、分岐先の式を評価します。
ifの値は、分岐先の式の値です。
elseを省略し条件式が偽であった場合のifの値は <code>()</code> です。
;構文:<syntaxhighlight lang=ebnf>
if-expr := if 条件式 '{' 式1 '}' [ else '{' 式2 ] '}'
</syntaxhighlight>
:;[https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=84117443c135c52137b9005717322e82 条件式に整数を使うと]:<syntaxhighlight lang=Rust line copy>
fn main() {
let i = 0;
if i {
println!("zero");
}
}
</syntaxhighlight>
:;コンパイルエラー:<syntaxhighlight lang=text>
error[E0308]: mismatched types
--> src/main.rs:4:8
|
4 | if i {
| ^ expected `bool`, found integer
For more information about this error, try `rustc --explain E0308`.
</syntaxhighlight>
:: Rustでは、ifに限らず、条件式は、bool 型でなければいけません。
;[https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=2c8960df447a87aaeaecced963d20cde if.rs]:<syntaxhighlight lang=rust line copy>
fn main() {
let i = 0;
if i == 0 {
println!("零");
} else {
println!("非零");
}
let s = if i == 0 {
"ゼロ"
} else {
"非ゼロ"
};
println!("{}", s);
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
零
ゼロ
</syntaxhighlight>
:気をつけたいのは、式の値が参照されるifでは、それぞれの式(ここでは <code>"ゼロ"</code> と <code>"非ゼロ"</code>)にセミコロン<code> ; </code>を付けてはいけない点です。
:: もし セミコロン<code> ; </code> をつけると、ブロックの値は、Unit 型 <code>()</code> になります。
:: ifの、分岐先の2つの式の型は同じでなければいけません。
:: else節が省略された場合は、Unit 型を返さなければいけません。
::: 式の最後に、セミコロン<code>};</code> が必要ということです<ref>C言語系では、式を文にする為いに<code>};</code> が必要です。Rustもそう説明されている場合がありますが、Rustでは式の型の一致が目的です。</ref>。
:また、<syntaxhighlight lang=rust inline>let s = if i == 0 {</syntaxhighlight>の文末の<code>};</code> のセミコロンも忘れがちです。
:Rust では if に限らず、コードブロックは式の一種で値を返します<ref>コードブロックが値を持つプログラミング言語としては、BCPL, [[Ruby]], [[Scala]]や[[Kotlin]]があります。</ref>。その場合、コードブロックの最後の式がコードブロックの値となりセミコロン<code> ; </code>は不要で、もし、<code> ; </code>をつけると<code> ; </code>の次の式(=空文)の値<code> () </code>がコードブロックの値になります。
この特徴は、関数型プログラミングを意識したものですが、同時に後に説明する所有権の移譲で重要な役割を果たします。
===== if と else の連鎖 =====
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=e3c9a903bc6baab687f697a83bde8928 if-else.rs]:<syntaxhighlight lang=rust line copy>
fn main() {
let n = 0.0 / 0.0;
if n < 0.0 {
println!("負の数");
} else if n > 0.0 {
println!("正の数");
} else if n == 0.0 {
println!("零");
} else {
println!("{n}");
}
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
NaN
</syntaxhighlight>
このコードの中で、次のことが行われています:
# <code>n</code> に NaN(Not a Number)を代入しています。<code>0.0 / 0.0</code> は浮動小数点数のゼロで割り算を表し、NaN を返します。
# <code>if</code> 文で <code>n</code> の値に応じて条件分岐しています。
# <code>n</code> が負の数より小さい場合は、"負の数" と出力します。
# <code>n</code> が正の数より大きい場合は、"正の数" と出力します。
# <code>n</code> がゼロと等しい場合は、"零" と出力します。
# それ以外の場合は、<code>n</code> の値を <code>{n}</code> という形式で出力します。
しかし、Rustの浮動小数点数型では、NaN は <code><</code> や <code>></code> 、<code>==</code> などの比較演算子によって、他の数値との大小や等価性を比較することはできません。なぜなら NaN は "not a number" であり、数値としての大小関係が定義されていないためです。
そのため、このコードでは <code>n</code> が NaN である場合、どの条件にも合致せず、最後の <code>else</code> ブロックが実行されることになります。このブロックでは、<code>n</code> の値そのものを <code>{n}</code> という形式で出力しようとしています。
==== Some() ====
Rustでは、C言語のNULLに相当する値は None です。
通常の変数は None の代入は受付けませんが、Some() を使うと None を取り得る変数が宣言できます<ref>[https://doc.rust-lang.org/stable/std/option/enum.Option.html Option in std::option - Rust]</ref>。
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=afb9193c50a316090b6e3119f994f8cf some.rs]:<syntaxhighlight lang=rust line copy>
fn main() {
let mut x = Some(0);
println!("{:?}({})", x, type_of(x));
x = None;
println!("{:?}({})", x, type_of(x));
}
fn type_of<T>(_: T) -> &'static str {
std::any::type_name::<T>()
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
Some(0)(core::option::Option<i32>)
None(core::option::Option<i32>)
</syntaxhighlight>
Some() 及び None は、標準ライブラリ std::option で定義されていますが、初期化済みなので Use することなく、接頭辞Option::なしで使えます。
==== if let ====
Rustには、<code>if let</code>という特別な制御構造があります。<code>if let</code>は、<code>match</code>式を簡略化するために使用されます。<code>if let</code>式は、単一のパターンを使用して、変数が指定された値にマッチした場合にのみ式を実行します。<code>if let</code>を使用すると、冗長なマッチングのコードを削減できます。
以下は、<code>if let</code>を使用した例です。
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=0614af1127dbc6ec0d871443b16e6a67 if-let.rs]:<syntaxhighlight lang=rust style="width:40em">
fn main() {
let mut v = vec![2, 3, 5];
if let Some(x) = v.pop() {
println!("x = {}", x)
} else {
println!("done!")
}
if let Some(x) = v.pop() {
println!("x = {}", x)
} else {
println!("done!")
}
if let Some(x) = v.pop() {
println!("x = {}", x)
} else {
println!("done!")
}
if let Some(x) = v.pop() {
println!("x = {}", x)
} else {
println!("done!")
}
if let Some(x) = v.pop() {
println!("x = {}", x)
} else {
println!("done!")
}
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text style="width:40em">
x = 5
x = 3
x = 2
done!
done!
</syntaxhighlight>
: <code>.pop()</code>メソッドは、ベクターから最後の要素を取り出し、それを返します。ただし、ベクターが空である場合は<code>None</code>を返します。
: 最初の行では、<code>vec![2, 3, 5]</code>という3つの要素を持つベクターを作成しています。
: その後、5回の<code>if let</code>式を使用して、ベクターから要素を取り出し、それを表示するか、ベクターが空の場合には<code>done!</code>と表示します。
: 各<code>if let</code>式では、変数<code>x</code>を定義しています。
:: <code>v.pop()</code>の返り値が<code>Some</code>であれば、ベクターの最後の要素が変数<code>x</code>に束縛され、<code>println!</code>マクロを使用して<code>x</code>を表示します。
:: そうでない場合、すなわち、ベクターが空である場合は、<code>else</code>節が実行されて、<code>done!</code>が表示されます。
==== match ====
<code>match</code>は、値とパターンをマッチングして、対応するコードブロックを実行します。これは、複数の条件に基づいて処理を行う場合に非常に便利です。例えば、列挙型(enum)を使った状況では、<code>match</code>は特に有用です。
:<syntaxhighlight lang=rust copy>
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> u32 {
match coin {
Coin::Penny => {
println!("Lucky penny!");
1
},
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
</syntaxhighlight>
上記の例では、<code>Coin</code>型の列挙子を受け取り、それぞれのコインに対する価値を返す関数が定義されています。<code>match</code>は<code>Coin</code>の値を取り、各列挙子に対応するコードブロックを実行します。例えば、<code>Coin::Penny</code>の場合、"Lucky penny!"というメッセージが表示され、1が返されます。
<code>match</code>は、パターンに加えて<code>if</code>条件も組み合わせて使用できます。
:<syntaxhighlight lang=rust copy>
fn main() {
let some_value = Some(5);
match some_value {
Some(x) if x < 5 => println!("Less than 5: {}", x),
Some(x) => println!("Value: {}", x),
None => println!("No value"),
}
}
</syntaxhighlight>
この例では、<code>Some</code>型の値を持つ変数を<code>match</code>で処理しています。<code>if</code>条件は<code>Some</code>型であり、かつその値が5未満の場合にマッチします。それ以外の場合、単に値を表示します。
<code>match</code>はRustの強力なツールであり、パターンマッチングにより、安全性と表現力を向上させます。
=== 反復 ===
Rustには、以下のような反復構文があります。
# <code>[[#for|for]]</code>ループ
## <code>[[#iter()|iter()]]</code>メソッドを使用した反復処理
## <code>[[#enumerate()|enumerate()]]</code>メソッドを使用した反復処理
## <code>[[#zip()|zip()]]</code>メソッドを使用した反復処理
# <code>[[#while|while]]</code>ループ
# <code>[[#loop|loop]]</code>ループ
それぞれの構文について、詳しく解説していきます。
==== for ====
Rust の '''for''' は、指定された範囲内の値を反復処理するために使用されます。通常、配列、ベクトル、範囲、またはイテレータなどの反復可能オブジェクトに対して使用されます。
;構文:<syntaxhighlight lang=ebnf>
for_expression = "for" loop_variable "in" expression "{" statement* "}";
loop_variable = pattern;
expression = (expression_binop | expression_unop | expression) ;
pattern = identifier | "_" | literal ;
</syntaxhighlight>
: <code>for_expression</code>はforループを表し、 <code>loop_variable</code>は反復処理のために使用される変数、 <code>expression</code>は反復処理の対象となるデータのソース、 <code>statement</code>はループ内で実行される文を表します。 <code>pattern</code>は、ループ変数の型と一致するパターンを表します。
: <code>identifier</code>は、識別子の名前を表します。 <code>literal</code>は、文字列、数値、真偽値などのリテラル値を表します。
: forループは、 <code>loop_variable</code>によって定義された変数に <code>expression</code>で指定されたデータソースの値を順番に割り当て、それぞれの値に対して <code>statement</code>を実行します。
: <code>identifier</code>は識別子を、<code>literal</code>はリテラルをしめします。
===== Range =====
Rustにおける<code>range</code>は、範囲を表す型です。範囲の生成には2つの主要な方法があります。
;..(半開区間)
* <code>start..end</code>の形式で使用され、<code>start</code>から<code>end</code>の手前(<code>end</code>は含まれない)までの範囲を生成します。
例えば、<code>1..5</code>は1から4までの範囲を生成します。
; ..= (閉区間)
* <code>start..=end</code>の形式で使用され、<code>start</code>から<code>end</code>までの閉区間を生成します(<code>end</code>も含まれます)。
例えば、<code>1..=5</code>は1から5までの範囲を生成します。
これらの範囲は<code>for</code>ループの反復やイテレータの作成など、さまざまな場面で使用されます。例えば、<code>for</code>ループやイテレータを使って範囲内の要素を処理することができます。
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=a3cd190fbd860ce8efb0d6276fb0858a for-in-range.rs]:<syntaxhighlight lang=rust copy>
fn main() {
// 半開区間の使用例
for i in 1..5 {
println!("{i}"); // 1, 2, 3, 4が出力される
}
// 閉区間の使用例
for i in 1..=5 {
println!("{i}"); // 1, 2, 3, 4, 5が出力される
}
}
</syntaxhighlight>
範囲は整数や文字など、多くの型で使用できます。範囲の使用はイテレーションや特定の範囲内の操作を行う際に便利です。
===== iter() =====
<code>Rust</code>における<code>iter()</code>は、コレクション(ベクター、配列、ハッシュマップなど)をイテレート可能な形に変換するメソッドです。イテレータは、コレクション内の要素を1つずつ処理するための仕組みを提供します。
基本的な使い方は以下のようになります:
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=d45c8c0ad55adb01e50456dd7270983c iter.rs]:<syntaxhighlight lang=rust copy>
fn main() {
let vec = vec![1, 2, 3, 4, 5];
// ベクターのイテレータを作成する
let mut iter = vec.iter();
// イテレータを使って要素に順番にアクセスする
while let Some(value) = iter.next() {
println!("{value}");
}
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
1
2
3
4
5
</syntaxhighlight>
<code>iter()</code>メソッドは、イテレータを作成するための最も基本的な手段ですが、さまざまな応用もあります。
# <code>.map()</code>: イテレータを他の形に変換する場合に使われます。たとえば、各要素に対して関数を適用して新しいイテレータを作成します。
#:<syntaxhighlight lang=rust copy>
fn main() {
let vec = vec![1, 2, 3, 4, 5];
// 各要素を2倍する新しいイテレータを作成する
let doubled_iter = vec.iter().map(|x| x * 2);
for value in doubled_iter {
println!("{value}");
}
}
</syntaxhighlight>
# <code>.filter()</code>: 条件に一致する要素のみを含む新しいイテレータを作成します。
#:<syntaxhighlight lang=rust copy>
fn main() {
let vec = vec![1, 2, 3, 4, 5];
// 偶数の要素だけを含む新しいイテレータを作成する
let even_iter = vec.iter().filter(|&x| x % 2 == 0);
for value in even_iter {
println!("{value}");
}
}
</syntaxhighlight>
# <code>.fold()</code>: イテレータ内の要素を畳み込んで単一の値に集約します。
#:<syntaxhighlight lang=rust copy>
fn main() {
let vec = vec![1, 2, 3, 4, 5];
// 要素の合計を計算する
let sum = vec.iter().fold(0, |acc, &x| acc + x);
println!("合計: {sum}");
}
</syntaxhighlight>
# <code>.reduce()</code>: イテレータ内の要素を畳み込んで単一の値に集約します。
#:<syntaxhighlight lang=rust copy>
fn main() {
let vec = vec![1, 2, 3, 4, 5];
// 要素の合計を計算する
let sum = vec.into_iter().reduce(|acc, x| acc + x);
println!("合計: {:?}", sum);
}
</syntaxhighlight>
これらは<code>iter()</code>を基盤として利用する機能の一部です。<code>Rust</code>のイテレータは非常に強力で、関数型プログラミングの概念を取り入れながら、効率的で安全なコードを記述するための重要な手段となっています。
====== rev() ======
<code>iter()</code>メソッドに加えて、<code>rev()</code>メソッドを使用すると、要素を逆順で取り出すこともできます。
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=83d50bb12645221a766052413a9d73b8 iter-rev.rs]:<syntaxhighlight lang=rust copy>
fn main() {
let v = vec![1, 3, 5, 7, 11];
for x in v.iter().rev() {
println!("x = {}", x)
}
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
x = 11
x = 7
x = 5
x = 3
x = 1
</syntaxhighlight>
: このRustのコードは、 <code>for</code>ループを使用して、ベクトル <code>v</code>の要素を反復処理し、各要素の値を出力するものです。ただし、 <code>v</code>の要素を逆順に処理します。
: <code>v.iter().rev()</code>は、 <code>v</code>のイテレータを取得し、そのイテレータを逆順にするためのメソッドです。これにより、ベクトルの最後の要素から始まり、最初の要素まで逆順に反復処理します。
: <code>for</code>ループの本体では、 <code>println!</code>マクロを使用して、現在の <code>x</code>の値を表示しています。 <code>{}</code>はプレースホルダーであり、その場所に変数の値が挿入されます。この例では、 <code>{}</code>の中に <code>x</code>を指定して、ループの反復ごとに変化する <code>x</code>の値を表示しています。
===== enumerate() =====
<code>enumerate()</code>メソッドを使用すると、要素のインデックスと値を同時に取り出すこともできます。
;コード例:<syntaxhighlight lang=rust line copy>
let v = vec![1, 2, 3];
for (i, val) in v.iter().enumerate() {
println!("{}: {}", i, val);
}
</syntaxhighlight>
: このコードでは、<code>v.iter().enumerate()</code>メソッドを使用して、<code>v</code>のイテレータを作成し、各要素のインデックスと値を同時に反復処理しています。<code>for</code>ループの本体では、変数<code>i</code>を使ってインデックス、<code>val</code>を使って値を表示しています。
===== zip() =====
<code>zip()</code>メソッドを使用すると、複数のイテレータを同時に取り出すことができます。
;コード例:<syntaxhighlight lang=rust line copy>
let v1 = vec![1, 2, 3];
let v2 = vec!["one", "two", "three"];
for (i, val) in v1.iter().zip(v2.iter()) {
println!("{}: {}", i, val);
}
</syntaxhighlight>
: このコードは、<code>zip()</code>メソッドを使用して、2つのベクトルを同時に反復処理する方法を示しています。
: 最初の行で、整数のベクトル<code>v1</code>と文字列のベクトル<code>v2</code>を定義しています。
: 次に、<code>for</code>ループを使用して、<code>v1.iter()</code>と<code>v2.iter()</code>のイテレータを同時に取り出します。このとき、<code>(i, val)</code>というタプルの形式で、それぞれのイテレータから次の要素を取り出します。
: <code>println!</code>マクロの中で、<code>{}</code>に<code>i</code>と<code>val</code>をそれぞれ表示しています。<code>{}</code>の中にある<code>:</code>は区切り文字で、<code>i</code>と<code>val</code>を区別するために使用されています。
==== while ====
Rustにおいて、<code>while</code>は指定した条件式が<code>true</code>である限り、ブロック内のコードを繰返し実行する制御構文です。
;[https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=47dc5b7c9d866b3f5913a77b9a55988e while.rs]:<syntaxhighlight lang=rust copy>
fn main() {
let mut i = 0;
while i < 5 {
println!("i = {}", i);
i += 1
};
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
i = 0
i = 1
i = 2
i = 3
i = 4
</syntaxhighlight>
: このプログラムは、0から4までの数字を順番に表示するプログラムです。whileループを使用して、条件式<code>i < 5</code>が<code>true</code>である間、ループを継続します。ループの各イテレーションでは、<code>println!</code>マクロを使用して、変数<code>i</code>の現在の値を表示します。<code>i</code>の値は、ループ本文の最後で<code>i += 1</code>によって1ずつ増加します。条件式<code>i < 5</code>が<code>false</code>になったとき、ループが終了します。最終的に、0から4までの数字が順番に表示されます。
===== while let =====
Rustの<code>while let</code>は、ループ処理の一種で、パターンマッチングを行い、パターンにマッチする値をループ内で取り出しながらループを繰り返します。
<code>while let</code>は、<code>match</code>構文の糖衣構文で、一つの値を取り出してパターンマッチングを行い、パターンにマッチする場合は値を取り出し、マッチしない場合はループを終了します。
;[https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=0764d8389e3afc0854b4223b8db5a32b while-let.rs]:<syntaxhighlight lang=rust copy>
fn main() {
let mut n = Some(0);
while let Some(i) = n {
n = if i > 5 {
None
} else {
println!("i = {}", i);
Some(i + 1)
}
}
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
</syntaxhighlight>
: このコードは、<code>n</code> という <code>Option<i32></code> 型の変数を定義し、初期値を <code>Some(0)</code> として設定します。そして、<code>while</code> ループを使って <code>n</code> の値を取り出しながら、それが <code>None</code> でなければループを続けます。
: <code>while let</code> の条件式では、<code>n</code> が <code>Some(i)</code> とパターンマッチされ、<code>i</code> に値がバインディングされます。このパターンマッチにより、<code>n</code> が <code>Some(i)</code> でなければループは終了します。
: ループ本体では、<code>i</code> が <code>5</code> を超える場合は、<code>n</code> を <code>None</code> に更新してループを終了します。そうでなければ、<code>i</code> を表示して、<code>Some(i + 1)</code> を <code>n</code> に代入してループを継続します。
: このプログラムの出力は、0 から 5 までの数値が順番に表示されます。最後に <code>None</code> が表示されます。
===== while let とベクトル =====
Rustの<code>while let</code>は、反復可能な値に対して、パターンマッチングを行いながら反復処理を行うことも出来ます。ベクトルに対してwhile letを使うと、ベクトルの末尾から要素を取り出しながら反復処理を行うことができます。
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=6b252d13f67df88f533e55dab5d3397c while-let-again.rs]:<syntaxhighlight lang=rust copy>
fn main() {
let mut v = vec![1, 3, 5, 7, 11];
while let Some(x) = v.pop() {
println!("x = {}", x)
}
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
x = 11
x = 7
x = 5
x = 3
x = 1
</syntaxhighlight>
: このコードは、可変長配列<code>v</code>に値を追加し、whileループでその配列から値を取り出し、それを表示するものです。
: まず、可変長配列<code>v</code>に<code>vec![1, 3, 5, 7, 11]</code>という値を設定しています。その後、<code>while let</code>式を使って、<code>v.pop()</code>の戻り値が<code>Some(x)</code>である限り、ループが継続されます。<code>v.pop()</code>は、<code>v</code>の最後の要素を取り出し、その要素があれば<code>Some</code>で包んで返し、なければ<code>None</code>を返します。
: <code>while let</code>式では、取り出した値が<code>Some(x)</code>である場合に、<code>println!("x = {}", x)</code>を実行して<code>x</code>の値を表示します。
: つまり、このコードは<code>v</code>の末尾から要素を取り出しながら、取り出した要素の値を表示するという処理を続け、最後に配列が空になったらループを終了します。
{{コラム|Rustにdo-whileはありません|2=
Rustにdo-whileはありません。while の条件式に do のブロックを詰め込むことで同じことが実現できます。
;[https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=534d7cfaf805c7e8d99d4c1ba95ed3ab pseudo-do-while.rs]:<syntaxhighlight lang=rust highlight=6 line copy>
fn main() {
let mut i = 100;
while {
println!("{}", i);
i += 1;
i < 10
} {}
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
100
</syntaxhighlight>
:whileの条件式が省略されたかのように見えますが、4,5,6行目を含むブロックが、while の条件式となり値はブロックの最後の6行目の式の値です。
:7行目の {} がループ本体です。
;loopを使った例:<syntaxhighlight lang=rust line copy>
fn main() {
let mut i = 100;
loop {
println!("{}", i);
i += 1;
if !(i < 10) {
break;
}
}
}
</syntaxhighlight>
: loop のブロックの最後に脱出条件を書いた方がわかりやすいかもしれません。
}}
==== loop ====
<code>loop</code>は、明示的な条件式の指定がない限り、無限に繰り返されます。
;[https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=9349b3eff79e311c70c87d20942cf8f2 loop-and-break.rs]:<syntaxhighlight lang=rust copy>
fn main() {
let mut i = 0;
let result = loop {
if i > 3 {
break 100;
}
println!("i = {}", i);
i += 1;
};
println!("result = {}", result);
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
i = 0
i = 1
i = 2
i = 3
result = 100
</syntaxhighlight>
: 最初に、変数<code>i</code>を0に初期化します。そして、<code>loop</code>構文で、<code>i</code>が3より大きくなるまで繰り返します。
: 各反復中に、<code>if</code>文が使用されています。<code>if</code>の条件式は、<code>i</code>が3より大きくなった場合には<code>break 100;</code>が実行され、<code>loop</code>から脱出します。そうでない場合は、<code>println!()</code>関数を使用して、<code>i</code>の値を出力し、<code>i</code>を1増やします。
: 最後に、<code>loop</code>から脱出した後に<code>result</code>変数に格納される値は、<code>break</code>文の引数で指定された<code>100</code>です。そして、<code>println!()</code>関数を使用して、<code>result</code>の値を出力します。
: つまり、このコードは、<code>i</code>を0から3まで順に増やしながら、各値を出力し、<code>i</code>が3より大きくなったら100を返し、その後に<code>result</code>の値を出力するという処理を行っています。
==== continue ====
<code>continue</code> を実行すると、ループのブロック内の残りの処理がスキップされ、反復処理が続行されます。
;[https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=49f7412cec92162b9d77881e08f43138 continue.rs]:<syntaxhighlight lang=rust highlight=9 line copy>
fn main() {
let mut i = 0;
let result = loop {
if i > 10 {
break 100;
}
if i % 2 == 0 {
i += 1;
continue;
}
println!("i = {}", i);
i += 1;
};
println!("result = {}", result);
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
i = 1
i = 3
i = 5
i = 7
i = 9
result = 100
</syntaxhighlight>
: このコードは、0から10までの奇数を出力するプログラムです。
: まず、<code>i</code>変数を定義して0で初期化し、<code>result</code>変数を宣言しています。次に、<code>loop</code>キーワードでループを開始します。
: ループ本体では、<code>if</code>文を使って<code>i</code>が10より大きくなったら、<code>break</code>キーワードを使ってループを抜けます。これにより、11以上の奇数が出力されることはありません。
: 次に、<code>i</code>が偶数である場合は、<code>continue</code>キーワードを使ってループの先頭に戻ります。これにより、偶数はスキップされます。
: 最後に、<code>println!</code>マクロを使って現在の<code>i</code>の値を出力し、<code>i</code>を1増やします。
:ループが終了したら、最後に<code>result</code>の値を出力します。<code>break</code>キーワードが値を返すため、このループは<code>result</code>に100を設定します。
{{コラム|width=stretch|Rustのプログラミングでつまづきやすいところ|2=Rustはセーフティ、パフォーマンス、そしてエクスプレッションの柔軟性を組み合わせた高度なプログラミング言語です。初めてRustを使う場合、以下のようなトピックでつまづくことがよくあります。
#所有権と借用: Rustでは、各値には所有者があり、値のライフサイクルを追跡する必要があります。また、他のコードに値を渡すときは、その値を一時的に借りる必要があります。これらの概念が理解しづらい場合があります。
#ライフタイム: Rustでは、各値にはライフタイムがあり、その値が使用される期間を決定します。ライフタイムの概念が不明瞭になりがちで、特に複雑なデータ構造を扱う場合に問題が発生することがあります。
#パフォーマンスの最適化: Rustは高速な実行時パフォーマンスを提供しますが、それを達成するためには、手動でメモリ管理や最適化を行う必要があります。これらの最適化は、Rustの型システムと他の言語との違いを理解する必要があります。
#コンパイラエラー: Rustのコンパイラは非常に厳格で、コードがコンパイルできない場合があります。エラーメッセージは詳細で役に立ちますが、初めて見た場合は驚くかもしれません。
本書では、これらのトピックについて順次取り上げてゆきます。
}}
== 演算子 ==
Rustでは、演算子を利用して変数やリテラルの値を操作することができます。これらは算術計算、比較、論理操作、ビット操作など、さまざまな目的に応じて使用されます。Rustの演算子は、型安全性と明確性を重視して設計されており、他のプログラミング言語と似た点も多いですが、いくつかの独自の特性も持っています。
=== 算術演算子 ===
算術演算子は、数値型に対する基本的な計算を行います。これには以下が含まれます。
* <code>+</code>(加算):2つの値を加算します。
* <code>-</code>(減算):1つ目の値から2つ目の値を減算します。
* <code>*</code>(乗算):2つの値を掛け算します。
* <code>/</code>(除算):1つ目の値を2つ目の値で割ります。ただし、整数型の場合、結果は切り捨てられます。
* <code>%</code>(剰余):1つ目の値を2つ目の値で割った余りを求めます。
例:
:<syntaxhighlight lang=rust copy>
let a = 10;
let b = 3;
println!("加算: {}", a + b); // 出力: 加算: 13
println!("除算: {}", a / b); // 出力: 除算: 3
println!("剰余: {}", a % b); // 出力: 剰余: 1
</syntaxhighlight>
=== 比較演算子 ===
比較演算子は、値同士を比較し、<code>true</code>または<code>false</code>を返します。
* <code>==</code>(等価):2つの値が等しい場合に<code>true</code>。
* <code>!=</code>(非等価):2つの値が等しくない場合に<code>true</code>。
* <code><</code>(小なり):左の値が右の値より小さい場合に<code>true</code>。
* <code>></code>(大なり):左の値が右の値より大きい場合に<code>true</code>。
* <code><=</code>(小なりイコール):左の値が右の値以下の場合に<code>true</code>。
* <code>>=</code>(大なりイコール):左の値が右の値以上の場合に<code>true</code>。
例:
:<syntaxhighlight lang=rust copy>
let x = 5;
let y = 10;
println!("x < y: {}", x < y); // 出力: x < y: true
println!("x == y: {}", x == y); // 出力: x == y: false
</syntaxhighlight>
=== 論理演算子 ===
論理演算子は、真理値を操作します。
* <code>&&</code>(論理AND):両方が<code>true</code>のときに<code>true</code>。
* <code>||</code>(論理OR):どちらか一方が<code>true</code>のときに<code>true</code>。
* <code>!</code>(論理NOT):真理値を反転させます。
例:
:<syntaxhighlight lang=rust copy>
let a = true;
let b = false;
println!("a && b: {}", a && b); // 出力: a && b: false
println!("!a: {}", !a); // 出力: !a: false
</syntaxhighlight>
=== ビット演算子 ===
ビット演算子は、整数型に対してビット単位の操作を行います。
* <code>&</code>(ビットAND):対応するビットが両方とも1の場合に1。
* <code>|</code>(ビットOR):対応するビットのどちらかが1の場合に1。
* <code>^</code>(ビットXOR):対応するビットが異なる場合に1。
* <code><<</code>(左シフト):ビットを指定した数だけ左に移動。
* <code>>></code>(右シフト):ビットを指定した数だけ右に移動。
例:
:<syntaxhighlight lang=rust copy>
let n = 0b1100; // 12 (2進数)
println!("n << 1: {:b}", n << 1); // 出力: 11000 (24)
println!("n & 0b1010: {:b}", n & 0b1010); // 出力: 1000 (8)
</syntaxhighlight>
=== その他の演算子 ===
* <code>=</code>(代入):変数に値を代入します。ただし、Rustではイミュータブルな変数に対して代入はできません。
* <code>+=</code>、<code>-=</code>、<code>*=</code>、<code>/=</code>、<code>%=</code>:加算や減算などを行った結果を代入します。
例:
:<syntaxhighlight lang=rust copy>
let mut z = 5;
z += 3; // zは8になります
</syntaxhighlight>
Rustの演算子は型ごとに適切な制約が設けられており、不正な操作をコンパイル時に防ぐ仕組みが備わっています。これにより、安全かつ効率的なコードの記述が可能となります。
=== 演算子オーバーロード ===
Rustでは、一部の演算子をオーバーロードすることが可能です。演算子オーバーロードは、<code>std::ops</code>モジュールに定義されているトレイトを実装することで実現します。これにより、独自の型に対して演算子の動作を定義することができます。
たとえば、加算演算子(<code>+</code>)をオーバーロードするには、<code>std::ops::Add</code>トレイトを実装します。このトレイトには<code>add</code>メソッドが含まれ、演算の具体的な挙動を指定します。
==== 加算演算子のオーバーロード例 ====
以下は、ベクトル型に対して加算演算子をオーバーロードする例です。
:<syntaxhighlight lang=rust copy>
use std::ops::Add;
#[derive(Debug)]
struct Vector {
x: i32,
y: i32,
}
impl Add for Vector {
type Output = Vector;
fn add(self, other: Vector) -> Vector {
Vector {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
fn main() {
let v1 = Vector { x: 1, y: 2 };
let v2 = Vector { x: 3, y: 4 };
let v3 = v1 + v2; // 加算演算子が使用可能
println!("{:?}", v3); // 出力: Vector { x: 4, y: 6 }
}
</syntaxhighlight>
この例では、<code>Add</code>トレイトの<code>add</code>メソッドを実装することで、<code>Vector</code>型に対して<code>+</code>演算子を適用できるようにしています。
==== 他の演算子のオーバーロード ====
Rustでは他にも多くの演算子がトレイトとして定義されており、同様にオーバーロードが可能です。以下はその一部です。
* <code>std::ops::Sub</code>:減算演算子(<code>-</code>)
* <code>std::ops::Mul</code>:乗算演算子(<code>*</code>)
* <code>std::ops::Div</code>:除算演算子(<code>/</code>)
* <code>std::ops::Rem</code>:剰余演算子(<code>%</code>)
* <code>std::ops::Neg</code>:単項マイナス(<code>-</code>)
* <code>std::ops::BitAnd</code>:ビットAND(<code>&</code>)
* <code>std::ops::Shl</code>:左シフト(<code><<</code>)
==== 注意点 ====
演算子オーバーロードは便利ですが、過剰な使用はコードの可読性を損なう可能性があります。特に、直感的でない動作を定義する場合は、利用者に混乱を与えるおそれがあります。そのため、オーバーロードを使用する際は、型の意味や使用意図を明確に反映させることが重要です。
このように、Rustの演算子オーバーロード機能は、型安全性と柔軟性を両立させながら、独自の型に対して直感的な操作を実現する手段を提供します。
== パターンマッチング ==
=== パターンマッチングの概要 ===
==== パターンマッチングの基本概念 ====
パターンマッチングは、プログラミング言語において特定のパターンとデータを照合し、条件に基づいた処理を行う手法です。この手法は、コードの可読性や柔軟性を高めるために広く使用されます。パターンマッチングの基本的な概念は、与えられたデータが特定のパターンに一致するかどうかを検査し、それに応じた処理を行います。
==== Rustにおけるパターンマッチングの役割と重要性 ====
Rustでは、パターンマッチングは非常に重要な機能です。パターンマッチングは、異なる条件に基づいてコードをブロックに分割し、それぞれの条件に対して適切な処理を行うことができます。これにより、コードの複雑さが軽減され、可読性が向上します。また、Rustの型システムとの統合により、安全性やエラーハンドリングの向上も実現されます。
=== 基本的なパターン ===
==== リテラルパターン ====
リテラルパターンは、値そのものとマッチさせるための最も基本的なパターンです。以下の例では、<code>x</code>の値が<code>1</code>の場合にマッチします。
:<syntaxhighlight lang=rust copy>
let x = 1;
match x {
1 => println!("one"),
_ => println!("not one"),
}
</syntaxhighlight>
==== 特定の値とのマッチング ====
リテラルパターンは、数値や文字列、真理値などの様々なリテラル値とマッチさせることができます。
:<syntaxhighlight lang=rust copy>
let x = 'c';
match x {
'a' => println!("apple"),
'b' => println!("banana"),
'c' => println!("cherry"), // この行が実行される
_ => println!("other"),
}
</syntaxhighlight>
==== 変数パターン ====
変数パターンは、値を新しい変数にバインドするためのパターンです。以下の例では、<code>x</code>の値が<code>5</code>の場合に<code>y</code>に<code>5</code>がバインドされます。
:<syntaxhighlight lang=rust copy>
let x = 5;
match x {
y => println!("x is {}", y), // この行が実行され、y = 5
}
</syntaxhighlight>
==== 値を変数にバインドするパターン ====
変数パターンは、値をパターン内の変数にバインドすることができます。これは、値を後で使用したり、条件分岐に利用したりするのに便利です。
:<syntaxhighlight lang=rust copy>
let x = 10;
match x {
0 => println!("x is zero"),
y if y > 0 => println!("x is positive: {}", y), // この行が実行される
y => println!("x is negative: {}", y),
}
</syntaxhighlight>
==== ワイルドカードパターン ====
ワイルドカードパターン(<code>_</code>)は、任意の値とマッチします。これは、特定の値を無視したい場合や、残りのパターンを捕捉したい場合に便利です。
:<syntaxhighlight lang=rust copy>
let x = 42;
match x {
0 => println!("x is zero"),
_ => println!("x is something else"), // この行が実行される
}
</syntaxhighlight>
==== 任意の値とのマッチング ====
ワイルドカードパターンを使えば、任意の値とマッチさせることができます。これは、値を確認する必要がない場合や、デフォルトの処理を実行したい場合に役立ちます。
:<syntaxhighlight lang=rust copy>
let x = 123;
match x {
0 => println!("x is zero"),
_ => println!("x is not zero"), // この行が実行される
}
</syntaxhighlight>
=== 列挙型とパターンマッチング ===
==== 列挙型の定義と使い方 ====
Rustには、列挙型と呼ばれる特別な型があります。列挙型は、いくつかの列挙値のいずれかを取ることができる型です。以下の例では、<code>Direction</code>という列挙型を定義しています。
:<syntaxhighlight lang=rust copy>
enum Direction {
Up,
Down,
Left,
Right,
}
</syntaxhighlight>
列挙型の値を作成するには、列挙型名とコロン(<code>:</code>)を使用します。
:<syntaxhighlight lang=rust copy>
let up = Direction::Up;
let down = Direction::Down;
</syntaxhighlight>
==== 列挙型に対するパターンマッチングの活用 ====
列挙型とパターンマッチングを組み合わせると、非常に強力なコードを書くことができます。以下の例では、<code>Direction</code>列挙型の値に対してパターンマッチングを行っています。
:<syntaxhighlight lang=rust copy>
enum Direction {
Up,
Down,
Left,
Right,
}
fn get_direction_name(dir: Direction) -> &'static str {
match dir {
Direction::Up => "上",
Direction::Down => "下",
Direction::Left => "左",
Direction::Right => "右",
}
}
fn main() {
let up = Direction::Up;
let down = Direction::Down;
println!("up: {}", get_direction_name(up)); // 上
println!("down: {}", get_direction_name(down)); // 下
}
</syntaxhighlight>
この例では、<code>get_direction_name</code>関数が列挙型<code>Direction</code>の値に対してパターンマッチングを行い、対応する文字列を返しています。
=== 構造体とタプルのパターンマッチング ===
==== 構造体の定義と使い方 ====
Rustでは、構造体を使ってデータを表現することができます。構造体は、フィールドと呼ばれる複数の値を持つことができます。以下の例では、<code>Person</code>という構造体を定義しています。
:<syntaxhighlight lang=rust copy>
struct Person {
name: String,
age: u32,
}
</syntaxhighlight>
構造体のインスタンスを作成するには、構造体名とフィールド値を指定します。
:<syntaxhighlight lang=rust copy>
let person = Person {
name: String::from("Alice"),
age: 30,
};
</syntaxhighlight>
==== タプルの定義と使い方 ====
タプルは、異なる型の値を含むことができる集合体です。タプルは括弧<code>()</code>で囲んで定義します。
:<syntaxhighlight lang=rust copy>
let tuple = (1, 3.14, "hello");
</syntaxhighlight>
タプルの要素にアクセスするには、インデックスを使用します。
:<syntaxhighlight lang=rust copy>
let x = tuple.0; // 1
let y = tuple.1; // 3.14
let z = tuple.2; // "hello"
</syntaxhighlight>
==== 構造体とタプルに対するパターンマッチングの活用 ====
構造体やタプルに対してパターンマッチングを行うことができます。これは、データの構造に基づいて処理を行う場合に非常に便利です。
:<syntaxhighlight lang=rust copy>
struct Person {
name: String,
age: u32,
}
fn print_person_info(person: Person) {
match person {
Person { name, age } => println!("名前: {}, 年齢: {}", name, age),
}
}
fn main() {
let alice = Person {
name: String::from("Alice"),
age: 30,
};
print_person_info(alice);
}
</syntaxhighlight>
この例では、<code>print_person_info</code>関数が<code>Person</code>構造体のインスタンスに対してパターンマッチングを行い、名前と年齢を出力しています。
タプルに対してもパターンマッチングを行うことができます。
:<syntaxhighlight lang=rust copy>
fn print_tuple_info(tuple: (u32, f64, &str)) {
match tuple {
(x, y, z) => println!("x: {}, y: {}, z: {}", x, y, z),
}
}
fn main() {
let tuple = (42, 3.14, "hello");
print_tuple_info(tuple);
}
</syntaxhighlight>
この例では、<code>print_tuple_info</code>関数がタプル<code>(u32, f64, &str)</code>に対してパターンマッチングを行い、その要素を出力しています。
=== パターンガード ===
==== パターンガードの概要 ====
パターンガードは、パターンマッチングに条件を追加するための機能です。パターンに一致する値に対して、追加の条件を指定することで、より柔軟な処理を行うことができます。
==== パターンガードの実装方法と使いどころ ====
パターンガードは、パターンに矢印(<nowiki><code>=></code></nowiki>)と条件式を追加することで実装します。条件式が真の場合のみ、パターンに一致したものとして処理されます。
:<syntaxhighlight lang=rust copy>
let x = 10;
match x {
2 | 5 | 10 if x % 2 == 0 => println!("x は偶数です"),
_ => println!("x は奇数です"),
}
</syntaxhighlight>
この例では、<code>x</code> が 2、5、10 のいずれかで、かつ偶数の場合は、"x は偶数です" と出力されます。
パターンガードは、様々な場面で活用することができます。
* 特定の条件を満たす値のみを処理したい場合
* エラー処理を行う場合
* 複雑なパターンを処理する場合
==== ネストしたパターン ====
==== ネストしたパターンの例 ====
ネストしたパターンは、複数のパターンを組み合わせたパターンです。パターンガードと組み合わせることで、より複雑な条件を処理することができます。
:<syntaxhighlight lang=rust copy>
enum Color {
Red,
Green,
Blue,
}
struct Point {
x: i32,
y: i32,
color: Color,
}
let point = Point { x: 10, y: 20, color: Color::Red };
match point {
Point { x, y, color: Color::Red } => println!("赤い点が ({}, {}) にあります", x, y),
Point { x, y, color } => println!("({}, {}) に {} 色の点がいます", x, y, color),
}
</syntaxhighlight>
この例では、<code>point</code> が <code>Point { x, y, color: Color::Red }</code> の形式の構造体である場合のみ、"赤い点が ({}, {}) にあります" と出力されます。<code>point</code> が <code>Point { x, y, color }</code> の形式の構造体である場合は、"({}, {}) に {} 色の点がいます" と出力され、<code>color</code> には <code>point</code> の <code>color</code> フィールドの値が格納されます。
==== ネストしたパターンの利点と注意点 ====
ネストしたパターンを使用すると、複雑な条件を処理しやすくなります。しかし、パターンが複雑になりすぎると、コードが読みづらくなる可能性があります。
==== エラーハンドリングとパターンマッチング ====
==== ResultやOptionとの組み合わせ ====
<code>Result</code> や <code>Option</code> などの型とパターンマッチングを組み合わせることで、エラーハンドリングを効率的に行うことができます。
:<syntaxhighlight lang=rust copy>
let result = read_file("filename.txt");
match result {
Ok(contents) => println!("ファイルの内容: {}", contents),
Err(error) => println!("エラー: {}", error),
}
</syntaxhighlight>
この例では、<code>read_file</code> 関数が成功した場合のみ、ファイルの内容が出力されます。失敗した場合は、エラーメッセージが出力されます。
==== エラーハンドリングにおけるパターンマッチングの有用性 ====
パターンマッチングを使用すると、エラー処理をより簡潔かつ分かりやすく記述することができます。また、エラーの種類ごとに異なる処理を行うこともできます。
=== 高度なパターンマッチング ===
==== パターンマッチングにおける複雑なパターンの扱い方 ====
パターンマッチングは、複雑なパターンを処理するために様々な機能を提供しています。
; パターンガード : パターンに一致する値に対して、追加の条件を指定することができます。
; ネストしたパターン : 複数のパターンを組み合わせたパターンを作成することができます。
; パターン分解 : パターンに一致した値を複数の変数に格納することができます。
; ガード付きパターン : パターンに一致する値に対して、条件分岐を実行することができます。
これらの機能を組み合わせることで、複雑なデータ構造を効率的に処理することができます。
==== 複数のパターンに一致する場合の処理方法 ====
複数のパターンに一致する場合は、パターンガードやネストしたパターンを使用して、どのパターンに一致するかを判別する必要があります。
== マクロ ==
Rustのマクロは、<code>macro_rules!</code>を使ったマクロと<code>proc_macro</code>を使ったプロシージャマクロの2種類があります。<code>macro_rules!</code>を使ったマクロはパターンマッチングを用いて簡易的なマクロを定義します。一方、<code>proc_macro</code>を使ったプロシージャマクロは、Rustのコードを受け取り、変換したり、新しいコードを生成するためのより柔軟なマクロです。
=== <code>macro_rules!</code>を使ったマクロ ===
<code>macro_rules!</code>は、パターンに基づいてマッチングし、そのパターンに一致した場合に指定されたコードを生成する簡易なマクロを定義します。
例えば、<code>vec!</code>マクロは、可変長のベクタを生成するマクロです。これは<code>macro_rules!</code>を使って次のように実装されています。
:<syntaxhighlight lang=rust line copy>
macro_rules! vec {
// パターンマッチで要素を取得して新しいベクタを生成
( $( $x:expr ),* ) => {
{
let mut temp_vec = Vec::new();
$(temp_vec.push($x);)*
temp_vec
}
};
}
fn main() {
// `vec!`マクロを使ってベクタを生成
let my_vec = vec![1, 2, 3, 4];
// 生成されたベクタを表示
println!("{:?}", my_vec);
}
</syntaxhighlight>
これは、<code>vec![1, 2, 3]</code>を使うと、<code>[1, 2, 3]</code>というベクタを生成します。このマクロは、<code>$( $x:expr ),*</code>のパターンに一致して、指定された式(<code>$x</code>)をベクタに挿入するコードを生成します。
=== <code>proc_macro</code>を使ったプロシージャマクロ ===
<code>macro_rules!</code>を使わずにマクロを定義する方法もあります。これは、プロシージャマクロ(<code>proc_macro</code>)を使用した方法です。<code>proc_macro</code>は、<code>macro</code>キーワードによって定義される関数の一種で、Rustのコードを受け取り、そのコードを操作して変換することができます。
例えば、<code>vec!</code>マクロを<code>macro_rules!</code>ではなく、プロシージャマクロとして定義する場合は、<code>proc_macro</code>を使います。
:<syntaxhighlight lang=rust line copy>
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Expr, parse_quote};
#[proc_macro]
pub fn my_vec(input: TokenStream) -> TokenStream {
// 入力をパース
let input_expr: Expr = parse_macro_input!(input);
// 入力を取得して新しいベクタを生成するコードを生成
let expanded = quote! {
{
let mut temp_vec = Vec::new();
temp_vec.push(#input_expr);
temp_vec
}
};
// TokenStreamに変換して返す
TokenStream::from(expanded)
}
</syntaxhighlight>
この例では、<code>proc_macro</code>として<code>my_vec</code>という新しいマクロを定義しています。<code>my_vec</code>は、<code>proc_macro</code>の関数として定義され、Rustのコードを受け取り、それを操作して新しいコードを生成します。
このプロシージャマクロを使うと、次のようにマクロを呼び出すことができます。
:<syntaxhighlight lang=rust line copy>
fn main() {
let my_vec = my_vec!(42);
println!("{:?}", my_vec); // [42]
}
</syntaxhighlight>
この例では、<code>my_vec!</code>マクロを使用して、引数として<code>42</code>を渡しています。このマクロは、引数を含むベクタを生成するもので、<code>my_vec!(42)</code>は<code>[42]</code>というベクタを生成します。
この方法では、<code>proc_macro</code>を使用して、<code>macro_rules!</code>を使わずに独自のマクロを定義できます。ただし、この方法では<code>proc_macro</code>と関連するライブラリ(<code>syn</code>、<code>quote</code>など)を使用する必要があります。
=== マクロ関数 ===
マクロ関数は、<code>macro_rules!</code>マクロを使って定義されます。
これは、マクロのパターンとそれに対する置換を定義するマクロです。
マクロの呼び出し元は、パターンにマッチする式を渡し、置換が適用されたコードが生成されます。
;マクロ関数の例:<syntaxhighlight lang=rust line copy>
macro_rules! say_hello {
() => {
println!("Hello, world!");
};
}
fn main() {
say_hello!();
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
Hello, world!
</syntaxhighlight>
;解説
:上記の例では、<code>say_hello!()</code>マクロを定義しています。これは、空の引数リストに対して<code>println!("Hello, world!")</code>を生成するマクロです。
=== マクロ属性 ===
マクロ属性はRustのコンパイル時にコードを修飾するために使用されます。
通常は、関数や構造体、列挙型、フィールドなどに適用されます。
マクロ属性を使用することで、コンパイル時に生成されるコードに追加の情報を提供できます。
;マクロ属性の例:<syntaxhighlight lang=rust line copy>
#[derive(Debug)]
struct MyStruct {
my_field: i32,
}
fn main() {
let my_struct = MyStruct { my_field: 42 };
println!("{:?}", my_struct);
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
MyStruct { my_field: 42 }
</syntaxhighlight>
;解説
:上記の例では、<code>#[derive(Debug)]</code>マクロ属性を使用しています。これは、<code>MyStruct</code>に<code>Debug</code>トレイトを実装するために必要なコードを生成するマクロ属性です。<code>println!</code>マクロで<code>my_struct</code>を表示する際に、<code>Debug</code>トレイトのメソッドを呼び出して情報を表示することができます。
== クレートとは ==
* '''Rustのパッケージ単位:''' Rustでは、コードのパッケージングや再利用のために「クレート」(Crate)という単位が使われます。1つのクレートには、1つ以上の関連するモジュールやデータ型が含まれます。
* '''Cargoによる管理:''' クレートは、RustのパッケージマネージャであるCargoによって管理されます。Cargoは、クレートの作成、ビルド、依存関係の解決などを自動化するツールです。
{{See also|Cargoハンドブック}}
=== クレートの構造 ===
典型的なRustのクレートは、次のような構造を持ちます:
:<syntaxhighlight lang=text>
my_crate/
├── src/
│ ├── main.rs
│ ├── lib.rs
│ └── other_module.rs
├── Cargo.toml
└── README.md
</syntaxhighlight>
* '''src/:''' クレートのソースコードを含むディレクトリ。
* '''src/main.rs:''' クレートを実行するためのエントリポイント。
* '''src/lib.rs:''' ライブラリとしてコンパイルされる場合に使用されるエントリポイント。
* '''src/other_module.rs:''' 他のモジュール。
* '''Cargo.toml:''' クレートのメタデータ、依存関係、ビルド構成などを含むファイル。
* '''README.md:''' クレートのドキュメント。
=== クレートの作成 ===
新しいクレートを作成する手順は以下の通りです:
# <code>cargo new</code>コマンドを使用して新しいクレートのディレクトリを作成します。
# <code>Cargo.toml</code>ファイルを編集して、クレートのメタデータを定義します。
# 必要に応じて、<code>src/main.rs</code>や他のモジュールを編集して、クレートのコードを実装します。
# <code>cargo run</code>コマンドを使用してクレートをビルドして実行します。
=== クレートの公開 ===
クレートを他の開発者と共有するためには、それをcrates.ioという公式のクレートレジストリに公開する必要があります。公開手順は以下の通りです:
# crates.ioにアカウントを作成します。
# <code>cargo login</code>コマンドを使用してcrates.ioにログインします。
# クレートのバージョン番号を更新します。
# <code>cargo publish</code>コマンドを使用してクレートを公開します。
=== まとめ ===
Rustのクレートは、コードのパッケージングや再利用を容易にする重要な概念です。Cargoを使用してクレートを管理し、必要に応じてcrates.ioに公開することで、他の開発者とクレートを共有することができます。
Rustにおいて、パッケージはクレートと呼ばれます。クレートは、Rustのコードやライブラリをパッケージ化して共有できるようにするための仕組みです。クレートは、依存関係を解決するためのメタデータと、コードとその他のファイルから構成されています。
Rustには、<code>crates.io</code>という公式のクレートレジストリがあり、開発者はそこでクレートを共有したり、他の開発者が作成したクレートを使用したりできます。
<!--
*クレートとは何か
**クレートの概要
**クレートとは何を提供するか
**クレートの種類(バイナリ、ライブラリ、依存関係)
*クレートの作成
**クレートの作成方法
**クレートの名前とバージョン管理
**クレートのファイル構造
**クレートのライセンス
*クレートのビルド
**Cargoの概要
**Cargoのインストールと初期化
**Cargo.tomlファイルの説明
*クレートのビルド方法
*クレートのテスト
**テストの重要性
**ユニットテストと統合テストの違い
**テストの書き方と実行方法
**テストの結果の確認方法
*クレートの公開
**クレートの公開方法
**crates.ioにクレートをアップロードする方法
**クレートのバージョン管理と更新方法
**クレートのドキュメントの生成と公開方法
*クレートの依存関係
**依存関係の概要
**依存関係の設定方法
**Cargo.lockファイルの役割
**依存関係のアップデート方法
*クレートのプロジェクト管理
**複数のクレートを管理する方法
**クレートの相互依存関係
**ワークスペースの作成と利用方法
**ワークスペースでの依存関係の管理方法
-->
== 代数的データ型 ==
Rustにおける代数的データ型は、構造体(<code>struct</code>)と列挙型(<code>enum</code>)を指します。これらは複雑なデータ構造を表現するのに役立ちます。
=== 構造体(struct) ===
構造体は異なる型のフィールドを持つことができ、それぞれのフィールドは名前を持ちます。例えば:
:<syntaxhighlight lang=rust copy>
struct Point {
x: i32,
y: i32,
}
impl Point {
fn new(x: i32, y: i32) -> Self {
Self { x, y }
}
fn print(&self) {
println!("x: {}, y: {}", self.x, self.y);
}
}
fn main() {
let origin = Point::new(0, 0);
origin.print();
}
</syntaxhighlight>
=== 列挙型(enum) ===
列挙型は、いくつかの異なるバリアント(variant)の中から選択することができます。それぞれのバリアントはデータを持つことができます。
:<syntaxhighlight lang=rust copy>
enum Shape {
Circle(f64), // 半径を持つ
Rectangle(f64, f64), // 幅と高さを持つ
Square(f64), // 1辺の長さを持つ
}
impl Shape {
fn area(&self) -> f64 {
match self {
Shape::Circle(radius) => std::f64::consts::PI * radius * radius,
Shape::Rectangle(width, height) => width * height,
Shape::Square(side) => side * side,
}
}
}
fn main() {
let shapes = vec![
Shape::Circle(5.0),
Shape::Rectangle(10.0, 20.0),
Shape::Square(15.0),
];
for shape in &shapes {
println!("Area: {}", shape.area());
}
}
</syntaxhighlight>
これらの代数的データ型は、Rustで柔軟なデータ構造を表現する際に役立ちます。
== 属性(Attribute) ==
Rustの属性(Attribute)は、コンパイラに対してコードに関する追加情報や指示を提供するための注釈です。これらはコードの振る舞いや最適化、データレイアウト、コード生成に影響を与えます。属性は<code>#[...]</code>の形式で記述され、コンパイラやコードの挙動を制御します。Rustでは、属性を使用することでコードの効率性や可読性を高め、特定の機能を拡張できます。
==== よく使用される属性 ====
# '''<code>#[derive(...)]</code>'''
#* 自動導出(Derive Attribute)は、特定のトレイトを自動的に実装するために使用されます。
#* 例: <code>#[derive(Debug)]</code>は、構造体や列挙型に<code>Debug</code>トレイトを自動的に実装させ、<code>println!</code>でその内容を表示可能にします。
# '''<code>#[cfg(...)]</code>'''
#* コンパイル時の条件を指定するために使います。条件に応じてコードの一部をコンパイルするかどうかを制御します。
#* 例: <code>#[cfg(target_os = "linux")]</code>は、ターゲットOSがLinuxの場合のみ有効です。
# '''<code>#[allow(...)]</code> / <code>#[deny(...)]</code> / <code>#[warn(...)]</code>'''
#* コンパイラ警告のレベルを制御します。これにより、警告を許可、エラー化、または警告として表示させることができます。
#* 例: <code>#[deny(warnings)]</code>は、すべての警告をエラーとして扱います。
# '''<code>#[repr(...)]</code>'''
#* データのレイアウト方法を指定します。構造体や列挙型のメモリ配置を変更するために使用されます。
#* 例: <code>#[repr(C)]</code>は、C言語と互換性のあるレイアウトを指定します。
# '''<code>#[inline]</code> / <code>#[noinline]</code>'''
#* インライン展開の制御を行います。関数呼び出しのオーバーヘッドを削減するために使われ、最適化のためにインライン展開を促進します。
#* 例: <code>#[inline(always)]</code>は、関数が常にインライン展開されるように指定します。
# '''<code>#[test]</code> / <code>#[bench]</code>'''
#* ユニットテストやベンチマークテストをマークするために使います。
#* 例: <code>#[test]</code>は関数をテスト対象として指定し、テストランナーによって自動的に実行されます。
# '''<code>#[macro_use]</code>'''
#* マクロを利用する際に、外部クレートからマクロをインポートするために使います。
#* 例: <code>#[macro_use] extern crate serde_json;</code>で、<code>serde_json</code>クレートのマクロを利用できます。
# '''<code>#[feature(...)]</code>'''
#* 実験的な機能を有効にするために使われます。安定版Rustで使用するには、<code>nightly</code>ビルドが必要です。
#* 例: <code>#[feature(proc_macro)]</code>はプロシージャマクロ機能を有効にします。
=== Debugトレイト ===
<code>Debug</code>トレイトは、構造体や列挙型などのデータ構造をフォーマットして表示するために使われます。このトレイトを実装することで、デバッグ情報を簡単に出力することができます。
==== 使用例 ====
:<syntaxhighlight lang=rust>
#[derive(Debug)]
struct MyStruct {
name: String,
age: u32,
}
fn main() {
let my_data = MyStruct {
name: String::from("Alice"),
age: 30,
};
// Debugトレイトを使用して構造体を表示
println!("My data: {:?}", my_data);
}
</syntaxhighlight>
;出力例:
:<syntaxhighlight lang=text>
My data: MyStruct { name: "Alice", age: 30 }
</syntaxhighlight>
このコード例では、<code>#[derive(Debug)]</code>を使って<code>MyStruct</code>構造体に<code>Debug</code>トレイトを実装し、<code>println!</code>マクロで<code>{:?}</code>フォーマット指定子を使って内容を表示しています。<code>Debug</code>トレイトを手動で実装することなく、簡単にデバッグ情報を出力できます。
=== その他の属性 ===
* '''<code>#[allow(dead_code)]</code>'''
** 使用されていないコードに対する警告を無視します。特にデバッグコードや一時的な実装で便利です。
* '''<code>#[cfg_attr(...)]</code>'''
** 条件付きで属性を適用するために使います。<code>#[cfg]</code>と組み合わせることで、特定の条件下で属性を追加できます。
* '''<code>#[must_use]</code>'''
** 戻り値が使われなければコンパイラから警告を出します。戻り値を意図的に無視することを防ぎます。
Rustの属性は、コードの最適化やコンパイラの挙動、エラーチェックを制御するために強力なツールです。それぞれの属性は特定の目的を持っており、正しく使用することでコードの品質を向上させることができます。
== ジェネリックス ==
Rustにおけるジェネリクスは、特定の型に依存せず、複数の型で動作するコードを作成するための重要な機能です。ジェネリクスを使用することで、同じコードを複数の型で再利用したり、型安全性を保ちながら柔軟性を持たせたりすることができます。
=== 基本的なジェネリクスの使用 ===
:<syntaxhighlight lang=rust copy>
// Tというジェネリックな型を持つ関数
fn print_value<T: std::fmt::Display>(value: T) {
println!("Value is: {}", value);
}
fn main() {
// 使用例
print_value(10); // Tはi32として推論される
print_value(2.73 as f32); // Tはf32として推論される
print_value("Hello"); // Tは&strとして推論される
}
</syntaxhighlight>
:この例では、<code>print_value</code>関数がジェネリックな型<code>T</code>を持ち、<code>T</code>は<code>std::fmt::Display</code>トレイトを実装している型に制限されています。<code>std::fmt::Display</code>トレイトは、<code>{}</code>でフォーマット可能な型を表します。
:引数<code>value</code>の型はコンパイル時に推論されます。
=== ジェネリックな構造体 ===
:<syntaxhighlight lang=rust copy>
// ジェネリックな構造体
struct Pair<T, U> {
first: T,
second: U,
}
fn main() {
// 使用例
let pair_of_int_and_str = Pair { first: 10, second: "Hello" };
println!("first = {:?}, second = {:?}", pair_of_int_and_str.first, pair_of_int_and_str.second);
}
</syntaxhighlight>
:<code>Pair</code>構造体は2つの異なる型を持つことができます。使用する際に具体的な型を指定することで、ジェネリックな構造体を作成できます。
=== ジェネリックなトレイト ===
:<syntaxhighlight lang=rust copy>
// ジェネリックなトレイト
trait Printable {
fn print(&self);
}
// TがPrintableトレイトを実装していることを要求する関数
fn print_trait<T: Printable>(item: T) {
item.print();
}
// 使用例
struct MyType;
impl Printable for MyType {
fn print(&self) {
println!("Printing MyType");
}
}
fn main() {
let obj = MyType;
print_trait(obj); // Printableトレイトを実装したMyTypeのインスタンスを受け取る
}
</syntaxhighlight>
:ここでは、<code>Printable</code>というジェネリックなトレイトを定義し、<code>print_trait</code>関数で<code>Printable</code>トレイトを実装した型<code>T</code>を受け取る方法を示しています。
ジェネリクスは、関数、構造体、列挙型、トレイトなどのRustのさまざまな要素で使用できます。これにより、柔軟性のあるコードを作成し、再利用性を高めることができます。
=== 型制約 ===
ジェネリックスにおいては、型制約(type constraint)は、ジェネリックな型パラメータに対して特定の条件やトレイトの制約を課すことを指します。これにより、ジェネリックな型が特定の性質を持つことを保証し、安全性を確保することができます。
Rustでは、<code>trait</code>を使用して型制約を実装します。例えば、<code>std::fmt::Display</code>トレイトを持つ型に制約を課したい場合、以下のように実装します。
:<syntaxhighlight lang=rust copy>
fn print_value<T: std::fmt::Display>(value: T) {
println!("Value is: {}", value);
}
</syntaxhighlight>
ここでは<code><T: std::fmt::Display></code>という構文を使用して、<code>T</code>が<code>std::fmt::Display</code>トレイトを実装している必要があることを宣言しています。この制約により、<code>print_value</code>関数は<code>std::fmt::Display</code>トレイトを実装した型に対してのみ呼び出しが可能になります。
また、複数のトレイト制約を持つこともできます。例えば、<code>std::fmt::Debug</code>と<code>std::fmt::Display</code>トレイトの両方を実装した型を要求する場合は、次のように書きます。
:<syntaxhighlight lang=rust copy>
fn print_value<T: std::fmt::Debug + std::fmt::Display>(value: T) {
println!("Value is: {:?}", value);
}
#[derive(Debug)]
struct Point<T> {
x: T,
y: T,
}
// Point<T>型に対するDisplayトレイトの実装
impl<T: std::fmt::Display> std::fmt::Display for Point<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Point({}, {})", self.x, self.y)
}
}
fn main() {
let int_point = Point { x: 5, y: 10 };
print_value(int_point); // `Point`型は`std::fmt::Debug`と`std::fmt::Display`を実装している
let float_point = Point { x: 1.5, y: 3.2 };
print_value(float_point); // 同様に、`Point`型は`std::fmt::Debug`と`std::fmt::Display`を実装している
}
</syntaxhighlight>
<code>T</code>が<code>std::fmt::Debug</code>と<code>std::fmt::Display</code>の両方を実装している必要があります。
これにより、ジェネリックなコードをより安全に、かつ特定の条件下で使用できるように制約を課すことができます。
== 例外処理 ==
Rustでは、<code>Result</code>型と<code>panic</code>による例外処理が一般的です。<code>Result</code>型は成功または失敗を表す列挙型で、<code>Ok</code>は成功時の値、<code>Err</code>はエラー時の情報を持ちます。
まず、<code>Result</code>型を使用した例を見てみましょう:
:<syntaxhighlight lang=rust copy>
use std::fs::File;
use std::io::{self, Read};
fn read_file_contents(file_path: &str) -> Result<String, io::Error> {
let mut file = File::open(file_path)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
Ok(contents)
}
fn main() {
let file_path = "/etc/hosts";
match read_file_contents(file_path) {
Ok(contents) => println!("File contents: {}", contents),
Err(e) => println!("Error reading file: {:?}", e),
}
}
</syntaxhighlight>
この例では、<code>read_file_contents</code>関数が<code>Result<String, io::Error></code>を返します。これは、成功時には文字列を<code>Ok</code>で、エラー時には<code>io::Error</code>を<code>Err</code>で返すことを示しています。ファイルを開いたり、読み込んだりするメソッドの呼び出しには<code>?</code>演算子を使用し、エラーが発生した場合は早期リターンしてエラーを返します。
<code>match</code>ブロックでは、<code>read_file_contents</code>の戻り値に対して<code>Ok</code>と<code>Err</code>の両方の可能性に対処し、それぞれ成功時の振る舞いとエラー時の振る舞いを定義しています。これにより、関数の呼び出し元で適切にエラーハンドリングを行うことができます。
=== except() ===
また、Rustには<code>panic!</code>マクロを使用してプログラムを異常終了させる方法もあります。これは致命的なエラーが発生した場合に使用されますが、エラーを適切に処理するために<code>Result</code>型を使うことが推奨されます。
:<syntaxhighlight lang=rust copy>
fn main() {
let v = vec![1, 2, 3];
let index = 5;
let value = v.get(index).expect("Failed to get the value at index");
println!("Value at index {}: {}", index, value);
}
</syntaxhighlight>
この例では、<code>get</code>メソッドは<code>Option</code>型を返し、<code>expect</code>メソッドは<code>Some</code>の場合は中の値を返し、<code>None</code>の場合は指定したメッセージとともにプログラムを<code>panic</code>させます。
ただし、<code>expect</code>を使う際には、<code>panic</code>が発生した際に表示されるメッセージは注意深く選ぶ必要があります。
=== panic!() ===
<code>panic!()</code>は通常、予期せぬ状況やプログラムの継続が不可能な状況で使われることがあります。以下はそのような例です。
:<syntaxhighlight lang=rust copy>
fn divide(a: i32, b: i32) -> i32 {
if b == 0 {
panic!("Attempted to divide by zero!"); // 0で割ろうとした場合、致命的なエラーとしてpanic!()を呼ぶ
}
a / b // 正常な割り算を行う
}
fn main() {
let dividend = 10;
let divisor = 0;
let result = divide(dividend, divisor); // divide関数を呼び出す
println!("Result of division: {}", result);
}
</syntaxhighlight>
この例では、<code>divide()</code>関数で<code>b</code>が0の場合に<code>panic!()</code>が呼ばれています。0で割ることは数学的に定義されていないため、これは致命的なエラーであり、プログラムの実行を中断させます。
<code>panic!()</code>は、このような状況に直面した場合に、プログラムを停止させるための手段として使われます。しかし、このようなエラーは通常、<code>if</code>文や<code>match</code>文などの条件分岐を使用して事前にチェックして、エラーハンドリングを行うことが推奨されます。
<code>Result</code>を使ってエラーハンドリングを行うことで、<code>panic!()</code>に頼らずにエラーを適切に処理できます。
:<syntaxhighlight lang=rust copy>
fn divide(a: i32, b: i32) -> Result<i32, &'static str> {
if b == 0 {
Err("Attempted to divide by zero!") // 0で割ろうとした場合、エラーをResultで返す
} else {
Ok(a / b) // 正常な割り算の結果をOkで返す
}
}
fn main() {
let dividend = 10;
let divisor = 0;
match divide(dividend, divisor) {
Ok(result) => println!("Result of division: {}", result),
Err(e) => println!("Error: {}", e), // エラーを適切に処理する
}
}
</syntaxhighlight>
<code>divide()</code>関数は<code>Result<i32, &'static str></code>を返し、0で割るエラーの場合には<code>Err</code>を、正常な計算の場合には<code>Ok</code>を返します。<code>match</code>ブロックでは、<code>Ok</code>と<code>Err</code>の両方のケースを処理し、エラーが発生した場合にはエラーメッセージを表示します。
このように<code>Result</code>型を使用することで、エラーハンドリングを柔軟かつ安全に行うことができます。<code>panic!()</code>に頼るよりも、エラーを予測し、適切に処理する方が望ましいです。
=== Option型 ===
<code>Option</code>型は、何らかの値が存在するかどうかを表現するRustの列挙型です。<code>Option</code>型は<code>Some</code>と<code>None</code>の2つのバリアントを持ち、<code>Some</code>は値が存在することを示し、<code>None</code>は値が存在しないことを示します。
<code>Option</code>型は、特定の操作が値を返さない可能性がある場合や、値が存在しない場合にエラーを返す代わりに<code>None</code>を返すために使われます。このような状況では、<code>Result</code>型を使用せずに、<code>Option</code>型が利用されることがあります。
以下は<code>Option</code>型の例です:
:<syntaxhighlight lang=rust copy>
fn divide(a: i32, b: i32) -> Option<i32> {
if b == 0 {
None // 0で割ろうとした場合、Noneを返す
} else {
Some(a / b) // 正常な割り算の結果をSomeで返す
}
}
fn main() {
let dividend = 10;
let divisor = 0;
match divide(dividend, divisor) {
Some(result) => println!("Result of division: {}", result), // Someの場合は値を表示
None => println!("Error: Division by zero!"), // Noneの場合はエラーメッセージを表示
}
}
</syntaxhighlight>
この例では、<code>divide()</code>関数は<code>Option<i32></code>を返し、0で割るエラーの場合には<code>None</code>を、正常な計算の場合には<code>Some</code>を返します。<code>match</code>ブロックでは、<code>Some</code>と<code>None</code>の両方のケースを処理し、<code>None</code>の場合はエラーメッセージを表示します。
<code>Option</code>型は、特に値が存在しないことが普通に起こり得る場面で、エラー処理や結果の取り扱いを行う際に有用です。例外的な状況ではなく、むしろ普通の操作の一部として考えられる「値の有無」を扱う際に利用されることが多いです。
== イテレーター ==
Rustの<code>Iterator</code>トレイトは、コレクションやデータのシーケンスを反復処理するための非常に強力な機能です。<code>Iterator</code>は、<code>next()</code> メソッドを提供し、それを使用して次の要素を返し、シーケンスの終わりに達した場合は <code>None</code> を返します。
基本的な使い方は次のようになります:
# イテレータの作成: コレクションやデータからイテレータを作成します。<code>iter()</code>や<code>into_iter()</code>、<code>iter_mut()</code>などのメソッドを使用して、それぞれイミュータブルな参照、所有権、ミュータブルな参照を使ったイテレータを取得できます。
#:<syntaxhighlight lang=rust copy>
let numbers = vec![1, 2, 3, 4, 5];
let mut iter = numbers.iter(); // イミュータブルなイテレータ
</syntaxhighlight>
# <code>next()</code>を使用した反復処理: <code>next()</code>メソッドを使って、イテレータから次の要素を取得します。
#:<syntaxhighlight lang=rust copy>
match iter.next() {
Some(number) => println!("Number: {}", number),
None => println!("End of sequence"),
}
</syntaxhighlight>
# forループを使用した反復処理: 一般的には、<code>for</code>ループを使用してイテレータを処理します。
#:<syntaxhighlight lang=rust copy>
for number in numbers.iter() {
println!("Number: {}", number);
}
</syntaxhighlight>
=== Iteratorトレイト ===
<code>Iterator</code>トレイトは、<code>map()</code>、<code>filter()</code>、<code>fold()</code>などの便利なメソッドも提供しており、これらを組み合わせることでデータを効果的に処理できます。
:<syntaxhighlight lang=rust copy>
let numbers = vec![1, 2, 3, 4, 5];
// 各要素を2倍して新しいベクターを作成する
let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect();
// 偶数のみをフィルタリングする
let evens: Vec<i32> = numbers.into_iter().filter(|&x| x % 2 == 0).collect();
// 全要素を合計する
let sum: i32 = numbers.iter().sum();
</syntaxhighlight>
<code>Iterator</code>トレイトは、Rustでのデータ処理を非常に柔軟で効率的にします。関数型プログラミングの考え方に基づいた強力な機能を提供しています。
=== 独自イテレータの実装 ===
<code>Iterator</code>トレイトを使用して独自のイテレータを実装する例を示します。
例えば、0から始まり、指定されたステップごとに増加するカウンターを作成するイテレータを実装してみましょう。
:<syntaxhighlight lang=rust copy>
struct Counter {
current: u32,
step: u32,
max: Option<u32>,
}
impl Counter {
fn new(start: u32, step: u32, max: Option<u32>) -> Counter {
Counter {
current: start,
step,
max,
}
}
}
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
match self.max {
Some(max) if self.current >= max => None,
_ => {
let result = self.current;
self.current += self.step;
Some(result)
}
}
}
}
fn main() {
let counter = Counter::new(0, 2, Some(10));
for num in counter {
println!("{}", num);
}
}
</syntaxhighlight>
この例では、<code>Counter</code>構造体は<code>Iterator</code>トレイトを実装しています。<code>next()</code>メソッドは、現在の値を返し、カウンターを指定されたステップで増分させます。また、<code>max</code>値が設定されており、その値に達するとイテレータは終了します。
<code>main()</code>関数内では、<code>Counter</code>イテレータを使用して0から10まで2ずつ増加する数列を生成しています。
<code>Iterator</code>トレイトを実装することで、独自のイテレータを作成し、柔軟な反復処理を行うことができます。
== impl ==
<code>impl</code>キーワードは、Rustで特定の型に対してメソッドやトレイトを実装するために使用されます。基礎から応用的な使い方まで見ていきましょう。
=== 基礎的な使用例 ===
; メソッドの実装
:<syntaxhighlight lang=rust copy>
struct MyStruct {
value: i32,
}
impl MyStruct {
// MyStruct型に対するメソッドの実装
fn new(value: i32) -> MyStruct {
MyStruct { value }
}
fn get_value(&self) -> i32 {
self.value
}
}
fn main() {
let instance = MyStruct::new(42);
println!("Value: {}", instance.get_value()); // メソッド呼び出し
}
</syntaxhighlight>
<code>impl</code>ブロック内で、<code>MyStruct</code>に対する<code>new</code>メソッドと<code>get_value</code>メソッドを実装しています。<code>main</code>関数でこれらのメソッドを使用しています。
=== 応用的な使用例 ===
; トレイトの実装
:<syntaxhighlight lang=rust copy>
trait Printable {
fn print(&self);
}
struct MyType {
value: i32,
}
impl Printable for MyType {
fn print(&self) {
println!("Value: {}", self.value);
}
}
fn main() {
let obj = MyType { value: 10 };
obj.print();
}
</syntaxhighlight>
{{コラム|width=100%|トレイトとインターフェイスやプロトコルとの類似性|2=トレイトは他の言語でのインターフェースやプロトコルに似ています。これらの概念は、異なる型や構造体が共通の振る舞いを持つことを許可し、それによってポリモーフィズム(多様性)を実現します。
;インターフェース
:共通の振る舞い: インターフェースは、異なるクラスが共通の振る舞いを持つことを保証します。他の言語では、この振る舞いはインターフェースで定義され、クラスはそれを実装します。
:メソッドの宣言: インターフェースでは、クラスが実装しなければならないメソッドの宣言が含まれます。
:多重継承の代替: インターフェースは多重継承の代替手段としても使われ、クラスは複数のインターフェースを実装することができます。
;プロトコル
:抽象的な振る舞いの定義: プロトコルは、特定の振る舞いや機能を表す抽象的な規約です。Swiftなどの言語ではプロトコルが使われ、クラスや構造体はそれらを適合させます。
:メソッドの要求: プロトコルは、適合する型によって実装されるメソッドやプロパティの要求を定義します。
:型の適合性の強化: プロトコルに適合することで、様々な型を同じ抽象的な概念に束縛することができます。
;Rustのトレイト
:共通の振る舞いの提供: トレイトは、構造体や他の型に共通の振る舞いを提供します。それぞれの型はトレイトを実装することで、その振る舞いを持つことができます。
:メソッドの宣言: トレイトではメソッドの宣言が行われ、それを実装することでトレイトが利用できるようになります。
:型間の相互運用性と柔軟性: トレイトは型間での相互運用性や柔軟性を提供し、異なる型が同じ振る舞いを共有することができます。
}}
=== ジェネリックな実装 ===
; ジェネリックな構造体に対する実装
:<syntaxhighlight lang=rust copy>
struct MyGeneric<T> {
value: T,
}
impl<T> MyGeneric<T> {
fn new(value: T) -> MyGeneric<T> {
MyGeneric { value }
}
fn get_value(&self) -> &T {
&self.value
}
}
fn main() {
let instance = MyGeneric::new(42);
println!("Value: {:?}", instance.get_value());
let instance_str = MyGeneric::new("hello");
println!("Value: {:?}", instance_str.get_value());
}
</syntaxhighlight>
このコードは、<code>MyGeneric</code>を<code>i32</code>型と<code>&str</code>型の両方でインスタンス化しています。ジェネリックなデータ構造体とそのジェネリックなメソッドを利用して、異なる型に対して同じメソッドを使用する様子を示しています。<code>println!</code>マクロ内の<code>{:?}</code>は、<code>Debug</code>トレイトを実装する型の値を表示するためのフォーマット指定子です。
=== 他の例 ===
; 単純なトレイトの実装
:<syntaxhighlight lang=rust copy>
trait Summary {
fn summarize(&self) -> String;
}
struct Book {
title: String,
author: String,
}
impl Summary for Book {
fn summarize(&self) -> String {
format!("{} by {}", self.title, self.author)
}
}
fn summarize_any(item: &impl Summary) -> String {
item.summarize()
}
fn main() {
let book = Book {
title: String::from("Harry Potter"),
author: String::from("J.K. Rowling"),
};
println!("Summary: {}", summarize_any(&book));
}
</syntaxhighlight>
この例では、<code>Summary</code>トレイトを実装した様々な型に対して共通の<code>summary_any</code>関数を使用して、異なる型の値に対して要約を取得できます。
== 関数 ==
Rustでの関数は、プログラム内で再利用可能なコードブロックを定義するために使われます。関数は特定のタスクを実行するための手続きを含み、必要に応じて引数を受け取り、結果を返すことができます。基本的な関数の定義と使用方法を以下に示します。
=== 関数の定義 ===
:<syntaxhighlight lang=rust copy>
// 関数の定義
fn add(a: i32, b: i32) -> i32 {
a + b // 最後の式の結果が自動的に返り値になる
}
</syntaxhighlight>
この例では、<code>add</code>という名前の関数が定義されています。<code>a</code>と<code>b</code>は整数型(<code>i32</code>)の引数を受け取ります。<code>-> i32</code>は関数が<code>i32</code>型の値を返すことを示しています。
=== 関数の呼び出し ===
:<syntaxhighlight lang=rust copy>
let result = add(3, 5);
println!("Result: {}", result); // "Result: 8"が出力される
</syntaxhighlight>
<code>add</code>関数は<code>3</code>と<code>5</code>を引数に取り、それらを足して<code>8</code>を返します。<code>println!</code>マクロを使ってその結果を出力しています。
=== 引数と戻り値 ===
* 引数: 関数に渡す値。関数の定義において、引数は型を指定する必要があります。
* 戻り値: <code>-></code>演算子を使って関数が返す値の型を指定します。Rustでは最後の式の結果が自動的に返り値となります。
=== パターンマッチングを使用した複数の戻り値 ===
Rustの関数は複数の値を返すこともできます。
:<syntaxhighlight lang=rust copy>
fn calculate(a: i32, b: i32) -> (i32, i32) {
(a + b, a - b)
}
let (sum, difference) = calculate(10, 5);
println!("Sum: {}, Difference: {}", sum, difference); // "Sum: 15, Difference: 5"が出力される
</syntaxhighlight>
=== 関数の機能 ===
* 再帰: 自分自身を呼び出すことができます。
* クロージャ: 無名の関数を作成し、変数にキャプチャさせることができます。
* ジェネリクス: 型を指定せずに関数を定義することができ、後から具体的な型を指定できます。
Rustの関数は安全性、速度、パターンマッチング、ジェネリクス、所有権など、言語の多くの特徴を活用しています。これらの特性は、Rustを強力なプログラミング言語にしています。
=== ライフタイム: ===
Rustの関数とライフタイムは、関連性がありますが、関数のシグネチャ内でライフタイムを使用することは必ずしも必要ではありません。しかし、関数が参照を含む場合や、ジェネリクスを使う場合には、ライフタイムの指定が必要になることがあります。
==== 関数内のライフタイム ====
:<syntaxhighlight lang=rust copy>
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
</syntaxhighlight>
この例では、<code>longest</code>関数は2つの文字列スライスを引数として受け取り、それらのうち長さが長い方の参照を返します。<code>'a</code>はライフタイムパラメータであり、2つの引数と返り値の参照のライフタイムが同じことを示しています。これにより、返される参照が有効なスコープを保証します。
==== ライフタイムの省略 ====
Rustでは、ライフタイムの省略規則があります。特定のパターンにおいては、コンパイラが暗黙的にライフタイムを推論することができます。例えば、次のような関数シグネチャではライフタイムの省略が行われます。
:<syntaxhighlight lang=rust copy>
fn longest(x: &str, y: &str) -> &str {
// ...
}
</syntaxhighlight>
このような場合、コンパイラは適切なライフタイムを自動的に推論します。ただし、ライフタイムの省略は特定の条件に限定され、全ての場面で使えるわけではありません。
ライフタイムは主に、参照の有効期間を指定するために使用され、特に関数内で参照を扱う際に重要な役割を果たします。関数が複数の参照を扱い、それらの有効期間を整理する必要がある場合には、ライフタイムの指定が必要になることがあります。
=== クロージャ: ===
Rustではクロージャも関数として扱われます。クロージャは自身のスコープ外の変数をキャプチャして利用できます。これは非常に便利で、関数よりも柔軟な振る舞いを提供します。
次のコード例では、クロージャと関数の組み合わせを使って、外部スコープの変数をキャプチャして利用する方法を示します。
:<syntaxhighlight lang=rust copy>
fn main() {
let base_number = 10;
// クロージャの定義
let add_to_base = |x| x + base_number;
let new_number = 7;
// クロージャの使用
let result = add_to_base(new_number);
println!("Result: {}", result); // "Result: 17"が出力される
}
</syntaxhighlight>
この例では、<code>base_number</code>という変数が<code>add_to_base</code>クロージャにキャプチャされ、後でクロージャ内で使用されています。クロージャは外部の変数をキャプチャすることができ、そのコンテキストを保持して使用できる点が関数とは異なる特徴です。
Rustの関数は、パターンマッチングやジェネリクス、ライフタイム、クロージャなどの機能と組み合わせて、安全で効率的なコードを記述するための強力なツールです。
== 高階関数 ==
Rustは、関数型プログラミングの要素を備えた汎用プログラミング言語です。Rustでは、関数を変数やデータ構造に格納したり、関数を関数のパラメータとして渡したりすることができます。これらの機能は、高階関数として知られています。
Rustで高階関数を使用すると、コードの再利用性と保守性を向上させることができます。また、コードをより簡潔で読みやすくすることもできます。
Rustで高階関数を使用する方法はいくつかあります。
=== 関数を変数に格納する ===
Rustでは、関数を変数に格納することができます。これにより、関数を繰り返し使用したり、関数を別の関数に渡したりすることができます。
:<syntaxhighlight lang=rust copy>
fn square(x: i32) -> i32 {
x * x
}
let square_function = square;
println!("The square of 5 is {}", square_function(5));
</syntaxhighlight>
このコードでは、square()関数をsquare_function変数に格納しています。その後、square_function()関数を呼び出すことで、square()関数と同じ結果を得ることができます。
=== 関数を関数のパラメータとして渡す ===
Rustでは、関数を関数のパラメータとして渡すことができます。これにより、関数を別の関数に処理させることができます。
:<syntaxhighlight lang=rust copy>
fn map_numbers(numbers: &[i32], f: fn(i32) -> i32) -> Vec<i32> {
let mut result = Vec::new();
for number in numbers {
result.push(f(number));
}
result
}
fn square(x: i32) -> i32 {
x * x
}
let numbers = [1, 2, 3, 4, 5];
let squared_numbers = map_numbers(numbers, square);
println!("The squared numbers are: {:?}", squared_numbers);
</syntaxhighlight>
このコードでは、map_numbers()関数は、numbers配列の各要素をf関数に渡し、その結果を新しい配列に格納します。
=== クロージャを使用する ===
Rustでは、クロージャを使用して高階関数を作成することができます。クロージャは、関数本体の一部として定義された関数です。クロージャは、変数やデータ構造を捕捉して、関数本体内で使用することができます。
:<syntaxhighlight lang=rust copy>
fn filter_numbers(numbers: &[i32], f: impl Fn(i32) -> bool) -> Vec<i32> {
let mut result = Vec::new();
for number in numbers {
if f(number) {
result.push(number);
}
}
result
}
fn is_even(x: i32) -> bool {
x % 2 == 0
}
let numbers = [1, 2, 3, 4, 5];
let even_numbers = filter_numbers(numbers, is_even);
println!("The even numbers are: {:?}", even_numbers);
</syntaxhighlight>
このコードでは、filter_numbers()関数は、numbers配列の各要素をfクロージャに渡し、その結果がtrueである場合、その要素を新しい配列に格納します。
Rustで高階関数を使用すると、コードの再利用性と保守性を向上させることができます。また、コードをより簡潔で読みやすくすることもできます。
== 標準ライブラリー ==
Rustの標準ライブラリー(Standard Library)は、Rustコンパイラにバンドルされており、基本的なデータ構造、OSとのやり取り、スレッド、ファイルI/O、ネットワーキングなどの多くの機能を提供しています。以下に、標準ライブラリーの主要なカテゴリを紹介します。
=== コレクション(Collections) ===
* <code>Vec</code>, <code>VecDeque</code>: ベクターや双方向キューなどの動的な配列。
* <code>HashMap</code>, <code>BTreeMap</code>: ハッシュマップやBツリーマップなどのキーと値のペアを保持するマップ。
* <code>HashSet</code>, <code>BTreeSet</code>: ハッシュセットやBツリーセットなどのユニークな値を保持するセット。
=== スレッドと同期(Concurrency) ===
* <code>std::thread</code>: スレッドの生成と操作を提供。
* <code>std::sync</code>: Mutex、Atomicなどの同期機能を提供するモジュール。
=== ファイルI/Oとネットワーキング(I/O and Networking) ===
* <code>std::fs</code>: ファイルシステムとの相互作用を可能にするモジュール。
* <code>std::net</code>: ネットワーキングのためのモジュール。
=== プリミティブ型(Primitive Types) ===
* <code>std::primitive</code>: Rustのプリミティブ型(整数、浮動小数点数など)の機能を提供。
=== OS相互作用とその他(OS Interactions and Miscellaneous) ===
* <code>std::env</code>: 環境変数の取得などのOS環境に関する操作。
* <code>std::time</code>: 時間に関する機能を提供。
=== 入出力(Input/Output) ===
* <code>std::io</code>: 標準入出力やバッファリング、ファイルI/OなどのI/O操作を提供。
=== 文字列処理(String Manipulation) ===
* <code>std::str</code>: 文字列の操作、変換、検索などの機能を提供。
=== メモリ管理(Memory Management) ===
* <code>std::alloc</code>: メモリの割り当てと解放のための機能。
=== コンパイラ支援(Compiler Support) ===
* <code>std::marker</code>: マーカートレイトを提供し、コンパイラへのヒントを与える。
これらは標準ライブラリーの一部であり、Rustの基本的な機能を提供しています。開発者はこれらのモジュールや機能を組み合わせて、安全で効率的なプログラムを構築できます。
== コードギャラリー ==
このコードギャラリーは、さまざまなRustの機能やパターン、ベストプラクティスを示すためのサンプルコード集です。
=== エラトステネスの篩 ===
:<syntaxhighlight lang=rust copy>
fn eratosthenes(n: usize) {
let mut sieve = vec![true; n + 1];
sieve[0] = false;
sieve[1] = false;
for i in 2..=n {
if sieve[i] {
// jをi*iから始める
for j in (i * i..=n).step_by(i) {
sieve[j] = false;
}
}
if i * i >= n {
break;
}
}
for i in 2..=n {
if sieve[i] {
println!("{}", i);
}
}
}
fn main() {
eratosthenes(100);
}
</syntaxhighlight>
このRustのコードは、エラトステネスの篩を使用して与えられた範囲内の素数を見つけるものです。
# <code>eratosthenes</code> 関数は、与えられた <code>n</code> までの素数を見つけるためのエラトステネスの篩アルゴリズムを実装しています。このアルゴリズムでは、最初に <code>n + 1</code> サイズの <code>sieve</code> というブール型の配列を作成します。この配列は、各インデックスが素数かどうかを示します。
# <code>sieve[0]</code> と <code>sieve[1]</code> は素数ではないので、それらを <code>false</code> に設定します。
# 2 から <code>n</code> までの各数について、その数が素数である場合は、その数の倍数を素数ではないとマークします。これにより、素数の倍数を持つ数は素数ではないことがわかります。
# <code>main</code> 関数では、<code>eratosthenes</code> 関数を呼び出し、100までの素数を見つけます。見つかった素数は画面に出力されます。
このアルゴリズムは素数を見つけるための効率的な方法の一つであり、与えられた範囲内の素数を見つけることができます。
=== 最大公約数と最小公倍数 ===
:<syntaxhighlight lang=rust copy>
fn gcd2(m: i32, n: i32) -> i32 {
if n == 0 {
m
} else {
gcd2(n, m % n)
}
}
fn gcd(ints: &[i32]) -> i32 {
ints.iter().cloned().fold(ints[0], gcd2)
}
fn lcm2(m: i32, n: i32) -> i32 {
m * n / gcd2(m, n)
}
fn lcm(ints: &[i32]) -> i32 {
ints.iter().cloned().fold(ints[0], lcm2)
}
fn main() {
println!("gcd2(30, 45) => {}", gcd2(30, 45));
println!("gcd(&[30, 72, 12]) => {}", gcd(&[30, 72, 12]));
println!("lcm2(30, 72) => {}", lcm2(30, 72));
println!("lcm(&[30, 42, 72]) => {}", lcm(&[30, 42, 72]));
}
</syntaxhighlight>
このコードは高階関数を利用しています。<code>fold</code>関数は特に重要で、与えられた配列内の要素に対して特定の操作を順番に適用することができます。
まず、<code>gcd</code>関数では、<code>ints</code>配列内の要素に対して<code>fold</code>関数を使って最大公約数(<code>gcd2</code>関数)を計算しています。<code>fold</code>は初期値として<code>ints[0]</code>を受け取り、各要素<code>value</code>に対して<code>gcd2</code>を適用し、次の要素に対して再帰的に<code>gcd2</code>を適用します。これにより、配列内のすべての要素の最大公約数が計算されます。
同様に、<code>lcm</code>関数も<code>fold</code>を利用しています。ここでは<code>lcm2</code>関数が利用され、各要素に対して最小公倍数を求めるための計算が行われます。
高階関数の利用により、配列内の要素に対して繰り返し処理を行う必要がある場合でも、シンプルで効率的なコードを書くことができます。
=== 二分法 ===
[[W:二分法|二分法]]
:<syntaxhighlight lang=rust copy>
fn bisection(low: f64, high: f64, f: impl Fn(f64) -> f64) -> f64 {
let x = (low + high) / 2.0;
let fx = f(x);
match () {
_ if (fx.abs() - 1.0e-10) < f64::EPSILON => x,
_ if fx < 0.0 => bisection(x, high, f),
_ => bisection(low, x, f),
}
}
fn main() {
let result1 = bisection(0.0, 3.0, |x| x - 1.0);
println!("{}", result1);
let result2 = bisection(0.0, 3.0, |x| x * x - 1.0);
println!("{}", result2);
}
</syntaxhighlight>
: [[旧課程(-2012年度)高等学校数学B/数値計算とコンピューター#2分法]]の例を Rust に移植しました。
このRustのコードは、二分法(bisection method)を使って与えられた関数の根を見つけるものです。
<code>bisection</code>関数は、<code>low</code>から<code>high</code>の範囲で与えられた関数 <code>f</code> の根を探します。<code>f</code> は <code>Fn(f64) -> f64</code> のトレイトを実装しており、実際の関数の定義は呼び出し時に与えられます。
この関数は再帰的に呼び出されます。与えられた区間 <code>[low, high]</code> の中央値 <code>x</code> を求め、その点での関数の値 <code>f(x)</code> を計算します。この値が非常に小さいか(ここでは <code>1.0e-10</code>未満)、または非常に近い数になるまで <code>low</code> または <code>high</code> を更新して、区間を狭めていきます。
<code>main</code>関数では、2つの異なる関数 <code>x - 1</code> と <code>x^2 - 1</code> に対して <code>bisection</code> 関数を呼び出して、それぞれの関数の根を探し、<code>println!</code> マクロを使って根を表示しています。
=== 構造体とメソッド ===
Rustにクラスはありませんが、構造体がメソッドを持つことが出来ます。
:<syntaxhighlight lang=rust copy>
#[derive(Debug)]
struct Hello {
s: String,
}
impl Hello {
fn new(s: &str) -> Hello {
// 空文字列の場合は "world" を使用
let s = if s.is_empty() { "world" } else { s };
Hello { s: s.to_string() }
}
fn to_string(&self) -> String {
format!("Hello {}!", self.s)
}
fn print(&self) {
println!("{}", self.to_string()); // 直接to_stringを呼び出す
}
}
fn main() {
let hello1 = Hello::new("");
hello1.print(); // to_string()を呼ぶ必要がなくなる
let hello2 = Hello::new("my friend");
hello2.print(); // 同上
println!(
"Hello.constructor.name => Hello\nhello1 => {:?}\nhello2.s => {}",
hello1, hello2.s
);
}
</syntaxhighlight>
このRustのコードは、<code>Hello</code>という名前の構造体を定義し、その構造体に関連するメソッドや、<code>main()</code>関数を含んでいます。
まず、<code>Hello</code>構造体は<code>String</code>型の<code>s</code>フィールドを持っています。<code>#[derive(Debug)]</code>アトリビュートは、この構造体に<code>Debug</code>トレイトを自動的に実装するようコンパイラに指示しています。<code>Debug</code>トレイトを実装することで、デバッグ目的で構造体の内容を出力できるようになります。
<code>impl Hello</code>ブロックでは、<code>Hello</code>構造体に対するメソッドが定義されています。
* <code>new</code>メソッドは、引数として文字列を受け取り、それが空文字列の場合はデフォルトの文字列 "world" を持つ<code>Hello</code>構造体を生成します。それ以外の場合は、引数で渡された文字列を使用して<code>Hello</code>構造体を作成します。
* <code>to_string</code>メソッドは、<code>Hello</code>構造体のインスタンスに対して、挨拶文を含む文字列を生成します。
* <code>print</code>メソッドは、<code>Hello</code>構造体のインスタンスの<code>s</code>フィールド(挨拶文)を標準出力に表示します。
<code>main()</code>関数では、<code>Hello</code>構造体を使ってインスタンスを生成し、メソッドを呼び出しています。<code>println!</code>マクロを使用して、構造体やそのフィールドをデバッグ出力しています。最後の<code>println!</code>マクロでは、<code>hello1</code>のデバッグ表示(<code>{:?}</code>)と<code>hello2</code>の<code>s</code>フィールドを出力しています。
=== 構造体とメソッド(2) ===
[[Go/メソッドとインターフェース]]からの移植です。
:<syntaxhighlight lang=go>
use std::f64::consts::PI;
#[derive(Debug)]
struct GeoCoord {
longitude: f64,
latitude: f64,
}
impl GeoCoord {
/// 地球の半径(キロメートル)
const EARTH_RADIUS: f64 = 6371.008;
/// 緯度・経度をラジアンに変換するための係数
const RADIAN_CONVERSION: f64 = PI / 180.0;
/// 2つの地理座標間の距離を計算する
fn distance(&self, other: &GeoCoord) -> f64 {
let lat_i = self.latitude * Self::RADIAN_CONVERSION;
let other_lat_i = other.latitude * Self::RADIAN_CONVERSION;
let long_diff_i = (self.longitude - other.longitude) * Self::RADIAN_CONVERSION;
let sin_lat = f64::sin(lat_i) * f64::sin(other_lat_i);
let cos_lat = f64::cos(lat_i) * f64::cos(other_lat_i);
let cos_long_diff = f64::cos(long_diff_i);
let distance = f64::acos(sin_lat + cos_lat * cos_long_diff) * Self::EARTH_RADIUS;
distance
}
}
impl std::fmt::Display for GeoCoord {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let (ew, ns, long, lat) = format_coordinates(self.longitude, self.latitude);
write!(f, "({}: {:.6}, {}: {:.6})", ew, long, ns, lat)
}
}
/// 緯度・経度の値を適切にフォーマットする関数
fn format_coordinates(longitude: f64, latitude: f64) -> (&'static str, &'static str, f64, f64) {
let (ew, long) = if longitude < 0.0 {
("西経", -longitude)
} else {
("東経", longitude)
};
let (ns, lat) = if latitude < 0.0 {
("南緯", -latitude)
} else {
("北緯", latitude)
};
(ew, ns, long, lat)
}
fn main() {
let sites = vec![
("東京駅", GeoCoord { longitude: 139.7673068, latitude: 35.6809591 }),
("シドニー・オペラハウス", GeoCoord { longitude: 151.215278, latitude: -33.856778 }),
("グリニッジ天文台", GeoCoord { longitude: -0.0014, latitude: 51.4778 }),
];
for (name, gc) in &sites {
println!("{}: {}", name, gc);
}
for i in 0..sites.len() {
let current_site = &sites[i];
let next_site = &sites[(i + 1) % sites.len()];
println!(
"{} - {}: {:.2} [km]",
current_site.0,
next_site.0,
current_site.1.distance(&next_site.1)
);
}
}
</syntaxhighlight>
=== 逆ポーランド記法の解析と評価 ===
{{先頭に戻る|title=コード・ギャラリーに戻る|label=コードギャラリー|style=border-top:1px solid gray;}}
逆ポーランド記法は、数式の演算子を後置記法で表現する方法です。通常の中置記法では演算子がオペランドの間に置かれますが、逆ポーランド記法では演算子がオペランドの後ろに置かれます。これにより、括弧や演算子の優先順位を考える必要がなくなり、計算機で容易に評価できる形式になります。
例えば、中置記法での式 <code>3 + 4 * 5</code> は、逆ポーランド記法では <code>3 4 5 * +</code> と表現されます。この記法では、演算子が対象のオペランドに対して順番に適用されます。
:<syntaxhighlight lang=rust copy>
enum Token {
Add,
Sub,
Mul,
Div,
Operand(i32),
}
impl Token {
fn evaluate(&self, stack: &mut Vec<i32>) -> Result<(), &'static str> {
match self {
Token::Add | Token::Sub | Token::Mul | Token::Div => {
if stack.len() < 2 {
return Err("Invalid expression: not enough operands for operator");
}
let operand2 = stack.pop().unwrap();
let operand1 = stack.pop().unwrap();
match self {
Token::Add => stack.push(operand1 + operand2),
Token::Sub => stack.push(operand1 - operand2),
Token::Mul => stack.push(operand1 * operand2),
Token::Div => {
if operand2 == 0 {
return Err("Division by zero");
}
stack.push(operand1 / operand2);
}
_ => unreachable!(),
}
}
Token::Operand(num) => {
stack.push(*num);
}
}
Ok(())
}
}
fn evaluate_expression(expression: &str) -> Result<i32, &'static str> {
let mut stack: Vec<i32> = Vec::new();
let tokens: Vec<Token> = expression
.split_whitespace()
.map(|token| {
if let Ok(parsed_num) = token.parse::<i32>() {
Token::Operand(parsed_num)
} else {
match token {
"+" => Token::Add,
"-" => Token::Sub,
"*" => Token::Mul,
"/" => Token::Div,
_ => unreachable!(),
}
}
})
.collect();
for token in tokens {
if let Err(err) = token.evaluate(&mut stack) {
return Err(err);
}
}
if stack.len() != 1 {
return Err("Invalid expression: too many operands or operators");
}
Ok(stack[0])
}
fn main() {
let expression = "5 3 2 * + 8 2 / -";
match evaluate_expression(expression) {
Ok(result) => println!("Result: {}", result),
Err(err) => println!("Error: {}", err),
}
}
</syntaxhighlight>
このコードは、<code>Token</code>という列挙型を使って逆ポーランド記法の式を評価する関数を実装しています。
まず、<code>Token</code>は<code>Add</code>、<code>Sub</code>、<code>Mul</code>、<code>Div</code>、<code>Operand</code>の5つのバリアントを持ちます。<code>Operand</code>は整数値を保持します。
<code>Token</code>には<code>evaluate</code>というメソッドが実装されています。このメソッドでは、<code>Token</code>の各バリアントに対する処理が行われます。<code>Add</code>、<code>Sub</code>、<code>Mul</code>、<code>Div</code>の場合は、スタックから2つの値を取り出して、それらを演算し結果をスタックに積みます。<code>Operand</code>の場合は、その値をスタックに積みます。
<code>evaluate_expression</code>関数では、与えられた式をトークン化して<code>Token</code>のベクターに変換し、それぞれのトークンに対して<code>evaluate</code>メソッドを実行します。各トークンの評価においてエラーが発生した場合、そのエラーメッセージが直ちに返されます。最終的に、スタックに残った値が1つでない場合もエラーが返されます。
<code>main</code>関数では、<code>evaluate_expression</code>の結果に応じて結果を出力するか、エラーを表示します。これにより、逆ポーランド記法の式を評価し、正常な結果またはエラーメッセージを表示できます。
=== 式を逆ポーランド記法に変換する(手書き) ===
{{先頭に戻る|title=コード・ギャラリーに戻る|label=コードギャラリー|style=border-top:1px solid gray;}}
:<syntaxhighlight lang=rust copy>
#[derive(Debug, Clone, Copy)]
enum Token {
Number(i32),
Plus,
Minus,
Multiply,
Divide,
}
fn main() {
let input = "12+34*56/78";
let tokens = parse_input(input);
let rpn = infix_to_rpn(tokens);
println!("{:?}", rpn);
}
fn parse_input(input: &str) -> Vec<Token> {
let mut tokens = Vec::new();
let mut num = String::new();
for c in input.chars() {
match c {
'0'..='9' => num.push(c), // 数字を収集
'+' | '-' | '*' | '/' => {
if !num.is_empty() {
let n = num.parse().unwrap();
tokens.push(Token::Number(n)); // 数字をトークンに追加
num.clear(); // 数字をリセット
}
// 演算子をトークンに追加
match c {
'+' => tokens.push(Token::Plus),
'-' => tokens.push(Token::Minus),
'*' => tokens.push(Token::Multiply),
'/' => tokens.push(Token::Divide),
_ => unreachable!(),
}
}
_ => panic!("Invalid character in input!"), // 無効な文字の場合
}
}
// 最後の数値をトークンに追加
if !num.is_empty() {
let n = num.parse().unwrap();
tokens.push(Token::Number(n));
}
tokens
}
fn infix_to_rpn(tokens: Vec<Token>) -> Vec<Token> {
let mut rpn = Vec::new();
let mut stack = Vec::new();
for token in tokens {
match token {
Token::Number(_) => rpn.push(token), // 数字はそのままRPNに追加
Token::Plus | Token::Minus | Token::Multiply | Token::Divide => {
while let Some(&top) = stack.last() {
if precedence(&token) <= precedence(&top) {
rpn.push(stack.pop().unwrap()); // 優先順位が高い演算子を出力
} else {
break; // 自分より優先順位が低い演算子が来たら中断
}
}
stack.push(token); // 演算子をスタックに追加
}
}
}
// スタックに残った演算子を全てRPNに追加
while let Some(op) = stack.pop() {
rpn.push(op);
}
rpn
}
fn precedence(token: &Token) -> i32 {
match token {
Token::Multiply | Token::Divide => 2,
Token::Plus | Token::Minus => 1,
_ => 0,
}
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
[Number(12), Number(34), Number(56), Multiply, Number(78), Divide, Plus]
</syntaxhighlight>
このコードは、与えられた文字列を逆ポーランド記法(RPN)に変換するプログラムです。以下にその構造を解説します:
# <code>Token</code> 列挙型: 数字と演算子を表す列挙型です。<code>Number</code> は数字を、<code>Plus</code>, <code>Minus</code>, <code>Multiply</code>, <code>Divide</code> はそれぞれ演算子を表します。<code>derive(Debug, Clone, Copy)</code> が付与されており、デバッグ表示やクローン、コピーが可能です。
# <code>parse_input</code> 関数: 与えられた文字列をトークンに分割します。数字の場合は文字列を数値に変換して <code>Token::Number</code> に、演算子の場合は対応する <code>Token</code> に変換し、それらを <code>Vec<Token></code> に収集します。
# <code>infix_to_rpn</code> 関数: 中置記法のトークンのベクターを逆ポーランド記法に変換します。スタックとRPNベクターを使用して、トークンを処理します。演算子の場合、スタックのトップとの優先順位を比較して、適切な順序でRPNに追加します。
# <code>precedence</code> 関数: 演算子の優先順位を返します。乗算と除算が優先され、それ以外の演算子は同じ優先順位です。
このコードは、入力された文字列を数値と演算子に分割し、それらを逆ポーランド記法に変換する機能を持っています。特定の演算子の優先順位を考慮しながら適切な順序で演算子を配置し、RPNを生成します。
=== 式を逆ポーランド記法に変換する(手書き:別解:再帰下降パーサー) ===
{{先頭に戻る|title=コード・ギャラリーに戻る|label=コードギャラリー|style=border-top:1px solid gray;}}
:<syntaxhighlight lang=rust copy>
#[derive(Debug, Clone, PartialEq)] // Cloneトレイトを追加
enum Token {
Number(f64),
Plus,
Minus,
Multiply,
Divide,
LParen,
RParen,
EOF,
}
struct Lexer<'a> {
input: &'a str,
position: usize,
}
impl<'a> Lexer<'a> {
fn new(input: &'a str) -> Lexer<'a> {
Lexer { input, position: 0 }
}
// 空白をスキップする
fn skip_whitespace(&mut self) {
while self.position < self.input.len() && self.input.chars().nth(self.position).unwrap().is_whitespace() {
self.position += 1;
}
}
// 次のトークンを取得する
fn next_token(&mut self) -> Token {
self.skip_whitespace();
if self.position >= self.input.len() {
return Token::EOF;
}
let current_char = self.input.chars().nth(self.position).unwrap();
self.position += 1; // 次の位置に進める
match current_char {
'+' => Token::Plus,
'-' => Token::Minus,
'*' => Token::Multiply,
'/' => Token::Divide,
'(' => Token::LParen,
')' => Token::RParen,
_ if current_char.is_digit(10) || current_char == '.' => {
let start = self.position - 1; // トークンの開始位置
while self.position < self.input.len() && (self.input.chars().nth(self.position).unwrap().is_digit(10) || self.input.chars().nth(self.position).unwrap() == '.') {
self.position += 1;
}
let number_str = &self.input[start..self.position];
Token::Number(number_str.parse::<f64>().unwrap())
}
_ => panic!("Invalid character found: {}", current_char),
}
}
}
struct Parser<'a> {
lexer: Lexer<'a>,
current_token: Token,
}
impl<'a> Parser<'a> {
fn new(mut lexer: Lexer<'a>) -> Parser<'a> {
let current_token = lexer.next_token();
Parser { lexer, current_token }
}
// トークンを消費する
fn eat(&mut self, token: Token) {
if self.current_token == token {
self.current_token = self.lexer.next_token();
} else {
panic!("Invalid syntax");
}
}
// 項を解析する
fn factor(&mut self) -> Vec<Token> {
match self.current_token {
Token::Number(num) => {
self.eat(Token::Number(num));
vec![Token::Number(num)]
}
Token::LParen => {
self.eat(Token::LParen);
let result = self.expr();
self.eat(Token::RParen);
result
}
_ => panic!("Invalid syntax"),
}
}
// 積項を解析する
fn term(&mut self) -> Vec<Token> {
let mut result = self.factor();
while matches!(self.current_token, Token::Multiply | Token::Divide) {
let op = self.current_token.clone();
self.eat(op.clone());
let mut next_factor = self.factor();
result.append(&mut next_factor);
result.push(op);
}
result
}
// 式を解析する
fn expr(&mut self) -> Vec<Token> {
let mut result = self.term();
while matches!(self.current_token, Token::Plus | Token::Minus) {
let op = self.current_token.clone();
self.eat(op.clone());
let mut next_term = self.term();
result.append(&mut next_term);
result.push(op);
}
result
}
}
// 逆ポーランド記法を生成する関数
fn generate_reverse_polish_notation(input: &str) -> Vec<Token> {
let lexer = Lexer::new(input);
let mut parser = Parser::new(lexer);
parser.expr()
}
fn main() {
let result = generate_reverse_polish_notation("12 + 34 * 56 / 78");
println!("{:?}", result);
}
</syntaxhighlight>
コードは、与えられた数式を逆ポーランド記法に変換するためのプログラムです。ここでは、Lexer(字句解析器)とParser(構文解析器)という2つの主要な構成要素があります。
; Token
: <code>Token</code> 列挙型は、数式をトークンに分割するために使用されます。数字、演算子、および括弧のトークンを定義します。
; Lexer
: <code>Lexer</code> は、与えられた数式文字列をトークンに分割する役割を担います。<code>next_token</code> メソッドは、数式の文字列を走査して、各文字がどの種類のトークンに対応するかを判断します。
; Parser
: <code>Parser</code> は、Lexer によって生成されたトークンのストリームを受け取り、逆ポーランド表記に変換します。再帰的に式を解析し、優先順位を考慮しながら、逆ポーランド表記のトークン列を生成します。
:* <code>factor()</code> メソッドは、数または括弧で始まる要素(ファクター)を解析します。
:* <code>term()</code> メソッドは、乗算と除算の演算子を解析します。
:* <code>expr()</code> メソッドは、加算と減算の演算子を解析します。
; generate_reverse_polish_notation 関数
: この関数は、与えられた数式文字列を逆ポーランド表記に変換します。Lexer を使ってトークンに分割し、Parser を使って逆ポーランド表記のトークン列を生成します。
; main 関数
: <code>generate_reverse_polish_notation</code> を使って、指定された式を逆ポーランド表記で出力します。
逆ポーランド記法は、演算子がオペランドの後ろに置かれるので、式を解析してトークン列に変換することで、演算の優先順位を反映した形で数式を表現することができます。
----
これらのコードは、与えられた数学式を逆ポーランド記法(Reverse Polish Notation, RPN)に変換する方法を示していますが、アプローチが異なります。
1番目のコードは、文字列を直接解析してトークンに分割し、その後逆ポーランド記法に変換しています。一方、2番目のコードは、字句解析器(lexer)とパーサー(parser)を使用して、トークンに分割し、その後パースして逆ポーランド記法に変換しています。
1番目のコードは、基本的な数値と演算子の処理に集中しています。一方で、2番目のコードは字句解析や構文解析の段階を厳密に分離しています。また、2番目のコードは小数点もサポートしており、より柔軟な数値表現を可能にしています。
どちらのコードも同じ目的を果たしていますが、アプローチの違いが見られます。1番目のコードはシンプルで直感的ですが、拡張性に欠けるかもしれません。
一方で、2番目のコードはより複雑ですが、より柔軟で拡張性があります。それぞれのコードには長所と短所がありますが、どちらも与えられた数式を逆ポーランド記法に変換する点では同等の結果を提供します。実際には2番めのコードは不動小数点数やカッコに対応しています。
=== 式を逆ポーランド記法に変換する(nom版) ===
{{先頭に戻る|title=コード・ギャラリーに戻る|label=コードギャラリー|style=border-top:1px solid gray;}}
:<syntaxhighlight lang=rust copy>
use nom::character::complete::{char, digit1};
use nom::combinator::map;
use nom::multi::many0;
use nom::sequence::delimited;
use nom::IResult;
#[derive(Debug, Clone, Copy)]
enum Token {
Number(i32),
Plus,
Minus,
Multiply,
Divide,
}
fn main() {
let input = "12+34*56/78";
let (_, tokens) = parse_input(input).unwrap();
let rpn = infix_to_rpn(tokens);
println!("{:?}", rpn);
}
fn parse_input(input: &str) -> IResult<&str, Vec<Token>> {
many0(parse_token)(input)
}
fn parse_token(input: &str) -> IResult<&str, Token> {
let (input, token) = delimited(
nom::character::complete::space0,
nom::branch::alt((
map(digit1, |s: &str| Token::Number(s.parse().unwrap())),
map(char('+'), |_| Token::Plus),
map(char('-'), |_| Token::Minus),
map(char('*'), |_| Token::Multiply),
map(char('/'), |_| Token::Divide),
)),
nom::character::complete::space0,
)(input)?;
Ok((input, token))
}
fn infix_to_rpn(tokens: Vec<Token>) -> Vec<Token> {
let mut rpn = Vec::new();
let mut stack = Vec::new();
for token in tokens {
match token {
Token::Number(_) => rpn.push(token),
Token::Plus | Token::Minus | Token::Multiply | Token::Divide => {
while let Some(top) = stack.last().copied() {
if precedence(&token) <= precedence(&top) {
rpn.push(stack.pop().unwrap());
} else {
break;
}
}
stack.push(token);
}
}
}
while let Some(op) = stack.pop() {
rpn.push(op);
}
rpn
}
fn precedence(token: &Token) -> i32 {
match token {
Token::Multiply | Token::Divide => 2,
Token::Plus | Token::Minus => 1,
_ => 0,
}
}
</syntaxhighlight>
このコードは、<code>nom</code>というパーサーコンビネータライブラリを使用して、与えられた文字列を解析し、トークンに分割する機能を持っています。前のコードと比較してみましょう。
# <code>parse_input</code> 関数: <code>many0</code>コンビネータを使って、<code>parse_token</code>を繰り返し適用し、入力文字列をトークンのベクターに変換します。<code>IResult</code>型を返します。
# <code>parse_token</code> 関数: <code>delimited</code>コンビネータを使用してトークンの前後のスペースを処理し、与えられた文字列を様々なルールにマッチングさせます。数字、演算子それぞれのパースを行い、<code>Token</code>列挙型のトークンを返します。
# <code>infix_to_rpn</code> 関数: 前のコードと同じですが、与えられたトークンのベクターを逆ポーランド記法に変換する機能を持っています。
このコードは、<code>nom</code>を使ってトークン分割を行い、より柔軟なパースを可能にしています。<code>nom</code>を使用することで、トークンのパースやスペースの処理など、より複雑なルールを柔軟に記述できるようになります。
=== 複素数式評価器 ===
{{先頭に戻る|title=コード・ギャラリーに戻る|label=コードギャラリー|style=border-top:1px solid gray;}}
:<syntaxhighlight lang=rust copy>
extern crate num_complex;
use num_complex::Complex;
#[derive(Debug, PartialEq, Clone)]
enum Token {
Number(Complex<f64>),
Plus,
Minus,
Multiply,
Divide,
LParen,
RParen,
EOF,
}
struct Lexer<'a> {
input: &'a str,
position: usize,
}
impl<'a> Lexer<'a> {
fn new(input: &'a str) -> Lexer<'a> {
Lexer { input, position: 0 }
}
fn skip_whitespace(&mut self) {
while self.position < self.input.len() && self.input.chars().nth(self.position).unwrap().is_whitespace() {
self.position += 1;
}
}
fn next_token(&mut self) -> Token {
self.skip_whitespace();
if self.position >= self.input.len() {
return Token::EOF;
}
let current_char = self.input.chars().nth(self.position).unwrap();
match current_char {
'+' => {
self.position += 1;
Token::Plus
}
'-' => {
self.position += 1;
Token::Minus
}
'*' => {
self.position += 1;
Token::Multiply
}
'/' => {
self.position += 1;
Token::Divide
}
'(' => {
self.position += 1;
Token::LParen
}
')' => {
self.position += 1;
Token::RParen
}
'i' => {
self.position += 1;
Token::Number(Complex::new(0.0, 1.0))
}
_ if current_char.is_digit(10) || current_char == '.' => {
let start = self.position;
while self.position < self.input.len()
&& (self.input.chars().nth(self.position).unwrap().is_digit(10)
|| self.input.chars().nth(self.position).unwrap() == '.')
{
self.position += 1;
}
let number_str = &self.input[start..self.position];
let number = number_str.parse::<f64>().unwrap();
if let Some('i') = self.input.chars().nth(self.position) {
self.position += 1;
Token::Number(Complex::new(0.0, number))
} else {
Token::Number(Complex::new(number, 0.0))
}
}
_ => panic!("Invalid character found: {}", current_char),
}
}
}
struct Parser<'a> {
lexer: Lexer<'a>,
current_token: Token,
}
impl<'a> Parser<'a> {
fn new(mut lexer: Lexer<'a>) -> Parser<'a> {
let current_token = lexer.next_token();
Parser { lexer, current_token }
}
fn eat(&mut self, token: Token) {
if self.current_token == token {
self.current_token = self.lexer.next_token();
} else {
panic!("Invalid syntax");
}
}
fn factor(&mut self) -> Complex<f64> {
let token = self.current_token.clone();
match token {
Token::Number(num) => {
self.eat(Token::Number(num));
num
}
Token::LParen => {
self.eat(Token::LParen);
let result = self.expr();
self.eat(Token::RParen);
result
}
_ => panic!("Invalid syntax"),
}
}
fn term(&mut self) -> Complex<f64> {
let mut result = self.factor();
while vec![Token::Multiply, Token::Divide].contains(&self.current_token) {
let token = self.current_token.clone();
match token {
Token::Multiply => {
self.eat(Token::Multiply);
let next_factor = self.factor();
result = result * next_factor;
}
Token::Divide => {
self.eat(Token::Divide);
let next_factor = self.factor();
result = result / next_factor;
}
_ => panic!("Invalid syntax"),
}
}
result
}
fn expr(&mut self) -> Complex<f64> {
let mut result = self.term();
while vec![Token::Plus, Token::Minus].contains(&self.current_token) {
let token = self.current_token.clone();
match token {
Token::Plus => {
self.eat(Token::Plus);
let next_term = self.term();
result = result + next_term;
}
Token::Minus => {
self.eat(Token::Minus);
let next_term = self.term();
result = result - next_term;
}
_ => panic!("Invalid syntax"),
}
}
result
}
}
fn main() {
let lexer = Lexer::new("(2+ 3i)*4i");
let mut parser = Parser::new(lexer);
let result = parser.expr();
println!("{:?}", result);
}
</syntaxhighlight>
このコードは、数式をパースして複素数を計算する簡単な計算機の基本的な実装です。Rustの機能を活用して、トークン列を生成するLexerと、そのトークン列を解析して計算を行うParserを定義しています。
<code>Token</code>は、パーサーが認識するトークンの種類を表すenumです。<code>Lexer</code>は文字列を受け取り、その文字列をトークンに分割する役割を果たします。各トークンは、演算子や数値、括弧などを表現しています。<code>Parser</code>は、Lexerが生成したトークン列を受け取り、それを解析して数式を計算します。
<code>Lexer</code>は空白をスキップし、文字列を一文字ずつ見ていき、トークン列を生成します。<code>Parser</code>はトークン列を再帰的に解析し、四則演算を行って複素数を計算します。演算子の優先順位や括弧の処理も考慮されています。
このコードは、入力文字列 <code>(2+ 3i)*4i</code> を受け取り、それを計算して結果を表示します。各段階でトークンが正しく識別され、演算子や数値が正しく解釈されることを期待しています。
コード内のパニックは、予期しないトークンや構文エラーがあった場合に発生します。これらのエラーは、コードが期待する形式に文字列が合致しなかった場合に発生します。
このコードを用いると、複雑な数式も計算できますが、入力の検証やエラー処理についてはまだ改善の余地があります。
=== 複素数 ===
:<syntaxhighlight lang=rust copy>
extern crate num_complex;
use num_complex::Complex;
fn main() {
// 複素数の作成
let a = Complex::new(3.0, 4.0);
let b = Complex::new(-2.0, 5.0);
println!("a: {a}");
println!("b: {b}");
println!("a + b: {}", a + b);
println!("a - b: {}", a - b);
println!("a * b: {}", a * b);
println!("a / b: {}", a / b);
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
a: 3+4i
b: -2+5i
a + b: 1+9i
a - b: 5-1i
a * b: -26+7i
a / b: 0.4827586206896552-0.7931034482758621i
</syntaxhighlight>
このコードは、<code>num-complex</code>クレートを使用して複素数を扱うRustの例です。
# <code>extern crate num_complex;</code> および <code>use num_complex::Complex;</code> は、<code>num-complex</code>クレートから<code>Complex</code>型を使えるようにするためのインポートです。
# <code>Complex::new(3.0, 4.0);</code> および <code>Complex::new(-2.0, 5.0);</code> は、実部と虚部を指定して複素数を作成しています。
# <code>println!("a: {a}");</code> および <code>println!("b: {b}");</code> は、複素数 <code>a</code> と <code>b</code> を出力しています。
b: {}", a * b);</code>、<code>println!("a / b: {}", a / b);</code> は、それぞれ複素数 <code>a</code> と <code>b</code> の加算、減算、乗算、除算を行っています。結果を文字列として出力しています。
修正されたコードは次のようになります:
== 改廃された技術 ==
Rustの改廃された技術や利用が推奨されない技術は、言語標準の進化、安全性の向上、エコシステムの成熟などによって置き換えられます。以下に、代表的な技術を示します。
=== try! マクロ ===
* '''サポート開始年:''' 2015年
* '''サポート終了年:''' 2018年(非推奨化)
; 廃止または衰退の理由
: より簡潔で表現力のある<code>?</code>演算子が導入され、エラーハンドリングがより簡単になりました。
; 代替技術
: <code>?</code>演算子の使用が推奨されます。
=== std::sync::Arc::get_mut_unchecked ===
* '''サポート開始年:''' 2015年
* '''サポート終了年:''' 2018年(非推奨化)
; 利用推奨されない理由
: 安全性の問題があり、データ競合を引き起こす可能性があります。
; 代替技術
: <code>get_mut()</code>または<code>make_mut()</code>の使用が推奨されます。
=== std::env::home_dir ===
* '''対象:''' ホームディレクトリのパス取得
* '''サポート終了年:''' 2019年(非推奨化)
; 廃止または衰退の理由
: プラットフォーム間での一貫性の問題と、エッジケースでの不正確な結果。
; 代替技術
: <code>dirs</code>クレートや<code>home</code>クレートの使用が推奨されます。
=== catch_unwind_safe ===
* '''サポート開始年:''' 2016年
* '''サポート終了年:''' 2018年(非推奨化)
; 廃止または衰退の理由
: 安全性の保証に関する問題と、より良い代替手段の存在。
; 代替技術
: <code>std::panic::catch_unwind</code>と適切なパニック安全性の設計が推奨されます。
=== std::error::Error::description ===
* '''サポート開始年:''' 2015年
* '''サポート終了年:''' 2019年(非推奨化)
; 廃止または衰退の理由
: エラーメッセージの国際化や詳細な文脈提供が困難でした。
; 代替技術
: Display実装とError::sourceメソッドの使用が推奨されます。
=== 古いRaw Pointersの特定の使用法 ===
* '''対象:''' 特定の生ポインタ操作
* '''サポート終了年:''' 継続的に改善
; 廃止または衰退の理由
: メモリ安全性とプログラムの正確性に関する問題。
; 代替技術
: 参照、Box、Rc、Arcなどの安全な所有権型の使用が推奨されます。
=== std::thread::scoped ===
* '''サポート開始年:''' 2015年
* '''サポート終了年:''' 2015年(早期に削除)
; 廃止または衰退の理由
: メモリ安全性の問題が発見され、より安全な代替手段が必要とされました。
; 代替技術
: <code>crossbeam::scope</code>や<code>rayon</code>のスコープ付き並列処理が推奨されます。
=== 古いマクロ定義構文 ===
* '''サポート開始年:''' 2015年
* '''サポート終了年:''' なし(ただし新構文推奨)
; 廃止または衰退の理由
: 可読性とデバッグの困難さ、より表現力のある新しい構文の登場。
; 代替技術
: <code>macro_rules!</code>の新しい構文やproc-macroの使用が推奨されます。
=== std::ascii::AsciiExt ===
* '''サポート開始年:''' 2015年
* '''サポート終了年:''' 2019年(非推奨化)
; 廃止または衰退の理由
: トレイトの設計が最適でなく、より良い代替手段が標準ライブラリに追加されました。
; 代替技術
: str、charの組み込みメソッドの使用が推奨されます。
=== 古いFuture実装 ===
* '''対象:''' futures 0.1系
* '''サポート終了年:''' なし(ただし非推奨)
; 廃止または衰退の理由
: async/await構文の導入により、より簡潔で理解しやすい非同期プログラミングが可能になりました。
; 代替技術
: futures 0.3系とasync/await構文の使用が推奨されます。
== ほかの言語からの移植例 ==
=== 順列・組合わせ ===
Goから[[Go/関数#順列・組合わせ|順列・組合わせ]]を移植
==== 順列 ====
;[https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=73612aa97bd4a1541a0313f38effde87 順列]:<syntaxhighlight lang=rust line copy>
fn permutation<T: Clone>(s: &[T], n: usize) -> Vec<Vec<T>> {
if s.is_empty() {
panic!("slice is nil");
}
if n == 1 {
let mut result = Vec::new();
for v in s.iter() {
result.push(vec![v.clone()]);
}
return result;
}
let mut result = Vec::new();
for (i, v) in s.iter().enumerate() {
let mut sf = Vec::new();
for (j, w) in s.iter().enumerate() {
if j != i {
sf.push(w.clone());
}
}
for w in permutation(&sf, n - 1) {
let mut v_w = vec![v.clone()];
v_w.extend_from_slice(&w);
result.push(v_w);
}
}
result
}
fn main() {
println!("{:?}", permutation(&[1, 2, 3], 1));
println!("{:?}", permutation(&[0, 1, 2], 2));
println!(
"{:?}",
permutation(&["abc".to_string(), "def".to_string(), "xyz".to_string()], 3)
);
println!("{:?}", permutation::<i32>(&[], 2));
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
[[1], [2], [3]]
[[0, 1], [0, 2], [1, 0], [1, 2], [2, 0], [2, 1]]
[["abc", "def", "xyz"], ["abc", "xyz", "def"], ["def", "abc", "xyz"], ["def", "xyz", "abc"], ["xyz", "abc", "def"], ["xyz", "def", "abc"]]
</syntaxhighlight>
;解説
:上記の移植において、主に以下の点に注意が必要でした。
:* ジェネリック型の宣言方法がGoとは異なるため、<code>func Permutation[T any](s []T, n int)</code> のような書き方はできません。Rustでは、<code>fn permutation<T: Clone>(s: &[T], n: usize)</code> のように、<code><T></code>の前に<code>:</code>を付けてジェネリック境界を宣言します。
:* Goの<code>make</code>は、新しい配列やスライスを作成するための組み込み関数ですが、Rustでは<code>Vec::with_capacity()</code>や<code>Vec::new()</code>を使用します。
:* <code>panic!("slice is nil")</code>は、Rustのパニック処理において、エラーメッセージを伴うパニックを発生させるために使用されます。
:* <code>Vec</code>に要素を追加するには、Goの<code>append</code>に相当するRustのメソッドである<code>Vec::push()</code>や、<code>Vec::extend_from_slice()</code>を使用します。また、<code>Vec</code>の要素数は、<code>len()</code>ではなく<code>len()</code>と<code>Vec::capacity()</code>の両方を使って取得する必要があります。
==== 組合わせ ====
;[https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=06e15597fcf3bdc585abeccd3edb9454 組合わせ]:<syntaxhighlight lang=rust line copy>
fn combination<T: Clone>(s: &[T], n: usize) -> Vec<Vec<T>> {
if s.is_empty() {
panic!("slice is empty");
}
if n == 1 {
let mut result = Vec::new();
for v in s {
result.push(vec![v.clone()]);
}
return result;
}
let mut result = Vec::new();
for i in 0..=(s.len() - n) {
let v = s[i].clone();
for w in combination(&s[i + 1..], n - 1) {
let mut res = vec![v.clone()];
res.extend(w);
result.push(res);
}
}
return result;
}
fn main() {
println!("{:?}", combination(&[1, 2, 3], 1));
println!("{:?}", combination(&[0, 1, 2], 2));
println!(
"{:?}",
combination(&["abc", "def", "xyz"], 3)
);
// println!("{:?}", combination(&[], 2)); 要素型が確定できない
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
[[1], [2], [3]]
[[0, 1], [0, 2], [1, 2]]
[["abc", "def", "xyz"]]
</syntaxhighlight>
;解説
:上記の移植において、主に以下の点に注意が必要でした。
:*Rustのジェネリック関数の型パラメータには制約が必要なため、<code>T</code>がクローン可能であることを示す<code>Clone</code>トレイトを指定する必要があります。
:*Goのスライスと異なり、Rustのスライスは要素数が0の場合にも安全であるため、<code>ErrNilSlice</code>に相当する処理は<code>slice.is_empty()</code>で判定することができます。
:*Goのスライスと異なり、Rustのスライスは範囲外アクセスがパニックを引き起こすため、再帰呼び出し時にはスライスの範囲を明示的に指定する必要があります。
{{See also|JavaScript/オブジェクト#順列を求めるメソッドを配列に追加する|JavaScript/オブジェクト#組合わせを求めるメソッドを配列に追加する}}
== 脚註 ==
<references />
== 外部リンク ==
{{Wikipedia|Rust (プログラミング言語)|Rust}}
* [https://www.rust-lang.org/ 公式サイト(英語)]
* [https://www.rust-lang.org/ja/ 公式サイト(日本語)]
* [https://doc.rust-jp.rs/book-ja/title-page.html The Rust Programming Language 日本語版]
* [https://doc.rust-lang.org/book/title-page.html The Rust Programming Language (英語版)]
== 参考文献 ==
* {{Cite
|author=Jim Blandy, Jason Orendorff
|title=プログラミングRust
|edition=第2版
|publisher=オライリージャパン
|isbn=978-4873119786
|date=2022年1月19日
}}
* {{Cite
|author=Jim Blandy, Jason Orendorff
|title=プログラミングRust
|edition=第1版
|publisher=オライリージャパン
|isbn=978-4873118550
|date=2018年8月10日
}}
[[Category:Rust|*]]
[[カテゴリ:プログラミング言語]]
{{NDC|007.64}}
70sswnlaq1yfa1h9s7js3px6hx8xiiv
264528
264527
2024-11-30T00:20:15Z
Ef3
694
/* エラーハンドリング */ s/例外処理/エラーハンドリング/2
264528
wikitext
text/x-wiki
{{Pathnav|メインページ|工学|情報技術|プログラミング}}
Rustは、安全性とパフォーマンスを兼ね備えたモダンなシステムプログラミング言語です。本書では、Rustの基礎から応用までを網羅し、実践的なプログラミングスキルの習得を目指します。Rustは初めてのプログラミング言語としても、既存のスキルをさらに強化するためにも最適です。その特徴である所有権システムや並行性モデルについても詳しく解説し、堅牢で効率的なプログラムを構築するための知識を提供します。一緒にRustの世界を探求し、その魅力を存分に体験していきましょう。
== はじめに ==
Rust(ラスト)は、高性能かつ安全な並行処理を実現するために設計されたマルチパラダイム汎用プログラミング言語です<ref>{{cite web
|url=https://graydon2.dreamwidth.org/247406.html
|title=Rust is mostly safety
|last=Hoare
|first=Graydon
|date=2016-12-28
|website=Graydon2
|publisher=Dreamwidth Studios
|access-date=2021-12-03
|archive-date=2019-05-02
|archive-url=https://web.archive.org/web/20190502181357/https://graydon2.dreamwidth.org/247406.html
|url-status=live }}</ref>。
Rustの構文はC++に似ており、ボローチェッカーを利用して参照の検証を行い、メモリ安全性を保証しています。ボローチェッカーはRustコンパイラによって提供される静的解析ツールで、所有権システムに基づいてコード内の不正な借用(ボロー)を検出します。これにより、メモリ管理においてガベージコレクターを使わずに安全性を実現し、場合によってはリファレンスカウントによるメモリ管理も行えます。
また、Rustはシステムプログラミング言語でありながら、関数型プログラミングの要素も取り入れ、低レベルのメモリ管理を可能にしています。これにより、高度な制御が求められるアプリケーション開発に適しており、実行時エラーの発生を抑えながら、安全で信頼性の高いコードを作成することができます。Rustを通じて、効率的かつ堅牢なプログラム構築のための新しい可能性を探求していきましょう。
{{コラム|width=100%|セルフホスティング|2=セルフホスティングとは、ソフトウェアが自分自身をコンパイルできる状態を指し、他のコンパイラを用意することなく、そのソフトウェア自体で再構築が可能であることを意味します。コンパイラにおけるセルフホスティングは、最初に構築されたコンパイラを用いて同じ言語で書かれたソースコードを再コンパイルし、新しいコンパイラを生成する手法です。このプロセスを繰り返すことで、コンパイラを改良したバージョンに更新していけるのが特徴です。
セルフホスティングは、信頼性と安定性を高める手段でもあり、自分自身をコンパイルできる状態であることがソフトウェアの品質向上に寄与します。また、コンパイラの開発者にとっても、その言語やコンパイラの動作原理や構造について理解を深める機会となり、より効率的でパフォーマンスの良いソフトウェアの開発につながります。Rustもセルフホスティングを実現しており、こうした継続的な改善を通じて、安全性とパフォーマンスの向上を目指しています。
}}
== クイックツアー ==
Rustはメモリ安全性、並行性、パフォーマンスの向上に焦点を当てたモダンなプログラミング言語です。以下のRustのクイックツアーで、基本的な概念とコード例を紹介します。
# 基本構文:
#: Rustプログラムは<code>main</code>関数から始まります。<code>println!</code> マクロを使って標準出力に文字列を出力できます。
#;[https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=65f8e3b00b49ca5c691cd61bfc32d1b7 hello.rs]:<syntaxhighlight lang=rust copy>
fn main() {
println!("Hello, world!");
}
</syntaxhighlight>
#;実行結果:<syntaxhighlight lang=text>
Hello, world!
</syntaxhighlight>
#: [https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=65f8e3b00b49ca5c691cd61bfc32d1b7 hello.rs]は、[https://play.rust-lang.org/ Playground]に作った、このプログラムへのリンクになっています。
# データ型:
#: Rustには整数、浮動小数点数、真偽値などの基本データ型があります。
#:<syntaxhighlight lang=rust copy>
let age: i32 = 25;
let salary: f64 = 50000.50;
let is_rust_fun: bool = true;
let message: &str = "Hello, Rust!";
</syntaxhighlight>
# 制御構造:
#: <code>if</code>、<code>else if</code>、<code>else</code> 文で条件分岐ができます。
#: <code>while</code> ループや <code>for</code> ループで繰り返し処理ができます。
#:<syntaxhighlight lang=rust copy>
let num = 10;
if num > 0 {
println!("Positive");
} else if num < 0 {
println!("Negative");
} else {
println!("Zero");
}
for i in 0..5 {
println!("Iteration {}", i);
}
</syntaxhighlight>
# 関数:
#: 関数は <code>fn</code> キーワードを使って宣言します。
#:<syntaxhighlight lang=rust copy>
fn add(a: i32, b: i32) -> i32 {
a + b
}
fn main() {
let result = add(5, 3);
println!("Sum: {}", result);
}
</syntaxhighlight>
# 所有権システム:
#: Rustは所有権ベースのメモリ管理を採用しており、値の所有権が明確に定義されています。
#:<syntaxhighlight lang=rust copy>
fn main() {
let s1 = String::from("Hello");
let s2 = s1; // s1の所有権がs2に移動する(所有権の転送)
// println!("{}", s1); // エラー!s1はもう有効ではない
println!("{}", s2); // 正常に動作
}
</syntaxhighlight>
# 構造体とメソッド:
#: 構造体はデータをまとめるためのカスタム型で、メソッドを持つことができます。
#:<syntaxhighlight lang=rust copy>
struct Car {
model: String,
year: u32,
}
impl Car {
fn display_info(&self) {
println!("Model: {}, Year: {}", self.model, self.year);
}
}
fn main() {
let my_car = Car {
model: String::from("TOYOTA 86"),
year: 2022,
};
my_car.display_info();
}
</syntaxhighlight>
ここでは、Rustの基本的な構文とコンセプトを簡単に紹介しました。
== rustcのバージョン確認 ==
やや力技ですが、Rustのコンパイラ rustc のバージョンをコードから確認できます。
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=db39d32bceac679dd79591501075d7f6 version.rs]:<syntaxhighlight lang=rust copy>
fn main() {
let version = std::process::Command::new("rustc")
.arg("--version")
.output()
.expect("Failed to get Rust version");
if version.status.success() {
let stdout = String::from_utf8_lossy(&version.stdout);
println!("Rust version: {}", stdout);
} else {
eprintln!("Failed to retrieve Rust version information");
}
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
Rust version: rustc 1.84.0-nightly (1e4f10ba6 2024-10-29)
</syntaxhighlight>
このコードは、Rustのプログラム内で<code>rustc --version</code>コマンドを実行し、その結果(Rustコンパイラのバージョン情報)を取得しています。
# <code>std::process::Command::new("rustc")</code>: <code>Command</code>構造体を使って新しいコマンドを作成しています。ここでは<code>rustc</code>というコマンドを実行するよう指定しています。
# <code>.arg("--version")</code>: <code>rustc</code>コマンドに<code>--version</code>引数を渡しています。これにより、Rustコンパイラのバージョン情報を取得するよう指示しています。
# <code>.output()</code>: <code>Command</code>を実行して、その結果を取得します。ここでは<code>--version</code>を指定した<code>rustc</code>コマンドを実行し、その出力を取得しています。
# <code>.expect("Failed to get Rust version")</code>: コマンドの実行が失敗した場合にエラーメッセージを表示します。
# <code>if version.status.success() { ... } else { ... }</code>: 実行結果の<code>status</code>をチェックして、コマンドが正常に終了したかどうかを確認します。もし成功していた場合は、コマンドの出力結果(Rustコンパイラのバージョン情報)を取得し、それを標準出力に表示します。もし失敗していた場合は、エラーメッセージを標準エラー出力に表示します。
このコードは、Rustのプログラム内で外部コマンドを実行してその出力を取得する方法を示しています。具体的には、Rustコンパイラのバージョン情報を取得してそれを表示する例です。
== コメント ==
Rustのコメントには、[[C言語]]/[[C++]]と同じく一行コメントと範囲コメントがあります。
;一行コメント: <code>//</code>から行末までがコメントと見なされます。
;範囲コメント
: <code>/*</code>から<code>*/</code>までがコメントと見なされます。
: ネストは許されません。
;コメントの例:<syntaxhighlight lang=rust copy>
/*
* プログラムのエントリーポイントは、main 関数です。
* 関数定義は fn から始まります。
*/
fn main() {
println!("Hello, world!"); // println! は関数ではなくマクロで、マクロは識別子の末尾に ! が付きます。
}
</syntaxhighlight>
== 変数と型 ==
Rustでは、変数を宣言する際にはデフォルトでimmutable(不変)です。変更可能な変数を宣言するには、<code>mut</code> キーワードを使用します。変数の型はコンパイラによって推論されることが一般的ですが、型を明示的に指定することもできます。
例えば、変数の宣言と型指定は以下のようになります:
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=8cddaf15c8b53acb5e0a2013a5cb8cc2 decl.rs]:<syntaxhighlight lang=rust copy>
fn main() {
// 型推論による変数の宣言
let x = 5; // 整数型 i32 として推論される
let y = 3.14; // 浮動小数点型 f64 として推論される
println!("x = {x}, y = {y}");
// 型を明示的に指定する
let z: i64 = 100; // 64ビット整数型 i64
println!("z = {z}");
}
</syntaxhighlight>
Rustの基本的なデータ型には以下があります:
* 整数型 (<code>i8</code>, <code>i16</code>, <code>i32</code>, <code>i64</code>, <code>i128</code>, <code>u8</code>, <code>u16</code>, <code>u32</code>, <code>u64</code>, <code>u128</code>など)
* 浮動小数点型 (<code>f32</code>, <code>f64</code>)
* 論理値型 (<code>bool</code>)
* 文字型 (<code>char</code>)
* ポインタ型
* タプル型
* 配列型
* 列挙型
* 構造体型
* 文字列型 (<code>&str</code>, <code>String</code>)
Rustは静的型付け言語であり、変数の型はコンパイル時に確定されます。型の安全性に対する厳格なチェックを行うため、コンパイル時に型の整合性が確認されます。これにより、メモリの安全性やスレッドセーフなコードを書く際の支援が期待できます。
=== 変数とミュータブル・イミュータブル ===
Rustでは、変数を宣言するにはキーワード '''let'''を使います。
ディフォルトでは[[#イミュータブル|イミュータブル]](''Immutable'';宣言後には代入不能)な変数が宣言されます。
[[#ミュータブル|ミュータブル]](''Mutable'';宣言後に代入可能)な変数を宣言するには、追加のキーワード '''mut''' を使います。
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=9032ae8999c6cb3bf5412988320e50d7 hello-variables.rs]:<syntaxhighlight lang=rust highlight=2 line copy>
fn main() {
let hello : &str = "Hello, world!";
println!("{}", hello);
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
Hello, world!
</syntaxhighlight>
:2行目の<syntaxhighlight lang=rust inline>let hello : &str = "Hello, world!";</syntaxhighlight>が変数宣言です<ref>文字リテラルであることを強調するなら<syntaxhighlight lang=rust inline>let hello : &'static str = "Hello, world!";</syntaxhighlight>とすべきだったかもしれません。</ref>。
::&str(文字列のスライスのリファレンス)を型とする変数 <var>hello</var> を宣言し、"Hello, world!"で初期化しています。
::Rustには強力な[[#型推論|型推論]]があり多くの場合不要ですが、<code>let 変数名 : 型名</code>の書式で型を伴い変数宣言することも出来ます。
mut をつけない場合には変数に「代入不能」と聞くと、C言語などを知っている人は「定数」を思い浮かべるかもしれませが、
Rustにおいて「定数」は, const 宣言された定数や, static 宣言されかつ mut で修飾されていない変数が相当します。
==== 型推論 ====
Rust では、変数宣言が初期値を伴っていた場合、変数の型を省略することができ、初期値の型が変数の型になります。
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=ac2b2d100ee8ea175178ebe9b1e26c61 hello-type-inference.rs]:<syntaxhighlight lang=rust highlight=2 line copy>
fn main() {
let hello = "Hello, world!";
println!("{hello}");
}
</syntaxhighlight>
;実行結果:上に同じ
==== イミュータブル ====
Rust では、値が一度変数に let で束縛されると変更できません。これをイミュータブルと言います。
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=d13ccba2d1b5efef4ca929012eece549 hello-immutable.rs]:<syntaxhighlight lang=rust highlight='2,4' line copy>
fn main() {
let hello : &str = "Hello, world!";
println!("{hello}");
hello = "Hello, rust!";
println!("{hello}");
}
</syntaxhighlight>
;コンパイル結果:<syntaxhighlight lang=text>
error[E0384]: cannot assign twice to immutable variable `hello`
--> src/main.rs:4:5
|
2 | let hello : &str = "Hello, world!";
| -----
| |
| first assignment to `hello`
| help: consider making this binding mutable: `mut hello`
3 | println!("{hello}");
4 | hello = "Hello, rust!";
| ^^^^^^^^^^^^^^^^^^^^^^ cannot assign twice to immutable variable
For more information about this error, try `rustc --explain E0384`.
error: could not compile `playground` (bin "playground") due to 1 previous error
</syntaxhighlight>
:イミュータブルな変数には、代入できないというコンパイルエラーです。
==== ミュータブル ====
代入可能、すなわちミュータブルにするためには、変数宣言にあたり '''let''' に続けて '''mut''' をつけます。
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=6bbb520249ebc44f7e341988dfad92a3 hello-mutable.rs]:<syntaxhighlight lang=rust highlight=2 line copy>
fn main() {
let mut hello : &str = "Hello, world!";
println!("{hello}");
hello = "Hello, rust!";
println!("{hello}");
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
Hello, world!
Hello, rust!
</syntaxhighlight>
==== 同じ変数名での宣言 ====
同一スコープで同じ変数名での宣言は可能です。
同じ型である必要はありません。ミュータブルであるかイミュータブルであるかも問いません。
'''同じ変数名での宣言によって、それまで変数に束縛されていた値への参照がなくなります。'''
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=0604ce8c08449bba14b64f80c405815d 同じ変数名での宣言]:<syntaxhighlight lang=rust highlight='2,4' line copy>
fn main() {
let hello : &str = "Hello, world!";
println!("{}", hello);
let hello = 154649;
println!("{}", hello);
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
Hello, world!
154649
</syntaxhighlight>
==== 定数 ====
Rustには2種類の定数があり、どちらもグローバルスコープを含む任意のスコープで宣言することができます。また、どちらも明示的な型を持っている必要があります。
* const: 不変の値
* static: 静的寿命を持つミュータブルな値 静的寿命は推論されるので、指定する必要はありません。
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=901ef30e5d150a91c9fed78d63a4971d 2種類の定数]:<syntaxhighlight lang=rust highlight='2,3' line copy>
const HELLO : &str = "Hello, world!";
static LANGUAGE: &str = "Rust";
fn main() {
println!("{HELLO}");
println!("{LANGUAGE}");
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
Hello, world!
Rust
</syntaxhighlight>
コードを書き換えてconst宣言や(ミュータブルな)static宣言された変数に代入をしようとすると、エラーになります。
==== パターン ====
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=9e938e1309c6ba12f760a6c6f0c9d210 pattern.rs]:<syntaxhighlight lang=rust line highlight=5 copy>
fn main() {
let (mut x, mut y) = (5, 29);
println!("x={x} y={y}");
(x, y) = (y, x);
println!("x={x} y={y}");
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
x=5 y=29
x=29 y=5
</syntaxhighlight>
このコードは、<code>x</code> と <code>y</code> の値を交換するRustの機能を示しています。
* 最初の行では、<code>x</code> に 5 を、<code>y</code> に 29 を代入しています。
* 次の行では、<code>println!</code> マクロでは、交換後の <code>x</code> と <code>y</code> の値を表示しています。
* 次の行では、<code>(x, y) = (y, x);</code> という操作を行っています。これは、タプルを使って複数の変数に同時に値を代入しています。この場合、<code>(y, x)</code> というタプルの中身を <code>(x, y)</code> に順番に代入しています。これにより、<code>x</code> の値に <code>y</code> の値が入り、<code>y</code> の値に <code>x</code> の値が入ります。これによって <code>x</code> と <code>y</code> の値が交換されます。
*最後の <code>println!</code> マクロでは、交換後の <code>x</code> と <code>y</code> の値を表示しています。
このコードは、Rustのタプルを使った多値代入の機能を示しています。
=== データ型 ===
Restには豊富なデータ型(''Data Types'')があり、それらを組み合わせて新しい型を作ることができます<ref>{{Cite web
|url=https://doc.rust-lang.org/book/ch03-02-data-types.html
|title=Data Types - The Rust Programming Language
|accessdate=2021/12/08
}}</ref>。
==== スカラー型(''Scalar Types'') ====
スカラー型は単一の値を表します。Rustには、整数、浮動小数点数、論理値、文字という4つの主要なスカラ型があります。
===== 整数型(''Integer Types'') =====
Rustの整数型は、符号の有無とビット幅から12種類のバリエーションがあります。
:{| class=wikitable
|+ Rustの整数型
!型名!!説明
|-
!i8
|符号付き8ビット整数
|-
!u8
|符号なし8ビット整数
|-
!i16
|符号付き16ビット整数
|-
!u16
|符号なし16ビット整数
|-
!i32
|符号付き32ビット整数
|-
!u32
|符号なし32ビット整数
|-
!i64
|符号付き64ビット整数
|-
!u64
|符号なし64ビット整数
|-
!i128
|符号付き128ビット整数
|-
!u128
|符号なし128ビット整数
|-
!isize
|符号付きでポインタと同じサイズの整数
|-
!usize
|符号なしでポインタと同じサイズの整数
|}
:isizeとusizeのビット幅はプロセッサーのアーキテクチャーによって定義され、32ビットプロセッサーならば32、64ビットプロセッサーならば64です。
===== 整数リテラル(''Integer literals'') =====
リテラル(''Literals'')とは、プログラミングのソースコードで使用される、数値や文字列などのデータを直接表現したものです。
:{| class=wikitable style="float:left"
|+ 様々な整数リテラル
!基数!!表現
|-
!10
|19_800
|-
!16
|0xbadbeef
|-
!8
|0o777
|-
!2
|0b101_111_011
|-
!バイト(u8のみ)
|b'Q'
|}
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=9c41862df3833ee7bd16169631d74de7 例]:<syntaxhighlight lang=rust style="float:left;width:24em; margin: 1em">
fn main() {
println!("{:?}", 19_800);
println!("{:x}", 0xbadbeef);
println!("{:o}", 0o777);
println!("{:b}", 0b101_111_011);
println!("{}", b'Q');
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text style="float:left;width:12em; margin: 1em">
19800
badbeef
777
101111011
81
</syntaxhighlight>
:<br style="clear:both">
:数値リテラルには、123u8 の様に型名をタイプサーフィックス(''type suffix'')として補うことで、ビット幅を明記できます(オプショナル)。
::指定されない場合は(バイト以外は)i32が仮定されます(isizeではありません)。
:数値リテラルには、読みやすさのため 9_281_636 のように、アンダースコア _ を補うことができます(オプショナル)。
なお、<code>{:x}</code>の<code>{x}</code>部分は[[#プレースホルダー|プレースホルダー]]の[[#フォーマッティング・トレイツ(Formatting traits)|ファーマッティング・トレイツ]]です。「x」なら16進数、oなら8進数、bなら2進数で出力します。
===== 浮動小数点数型(''Floating-Point Types'') =====
Rustには、浮動小数点数を表現するための2つの主要な型があります。それぞれの型は、IEEE-754規格に従っています。
# <code>f32</code>: 32ビットの単精度浮動小数点数型です。精度は約6桁です。
# <code>f64</code>: 64ビットの倍精度浮動小数点数型です。精度は約15桁です。
Rustでは、浮動小数点数リテラルを書く場合、デフォルトで <code>f64</code> 型になります。例えば、<code>3.14</code> という浮動小数点数リテラルは、<code>f64</code> 型の数値になります。
以下は、<code>f32</code> 型と <code>f64</code> 型の浮動小数点数の使用例です。
:<syntaxhighlight lang=rust copy>
fn main() {
// デフォルトでは f64 型になる浮動小数点数
let my_float1 = 3.14; // f64 型
// サイズを明示して f32 型にする
let my_float2: f32 = 2.718; // f32 型
// 浮動小数点数同士の計算
let sum = my_float1 + f64::from(my_float2); // f64 型にキャストして計算
println!("Sum: {}", sum); // f64 型の結果が出力される
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=rust copy>
Sum: 5.857999935150147
</syntaxhighlight>
浮動小数点数は、数値計算や科学的な計算など、精度が求められる場面で使用されます。しかし、浮動小数点数の性質や精度による注意が必要な場面もありますので、注意深く扱う必要があります。
===== 論理値型(''The Boolean Type'') =====
Rustにおける論理値型の型名は <code>bool</code> で、真の値は <code>true</code>、偽の値は <code>false</code> です。この型は非常に基本的で、条件分岐やブール演算などで使用されます。
以下は <code>bool</code> 型の使用例です。
:<syntaxhighlight lang=rust copy>
fn main() {
let is_rust_cool = true; // 真の値を持つ変数
let is_java_cool = false; // 偽の値を持つ変数
if is_rust_cool {
println!("Rust is cool!"); // 条件が true の場合に実行される
} else {
println!("Rust is not cool."); // 条件が false の場合に実行される
}
// 論理演算
let result = is_rust_cool && is_java_cool; // 論理積 (AND) の例
println!("Result of logical AND: {}", result); // false が出力される
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
Rust is cool!
Result of logical AND: false
</syntaxhighlight>
<code>bool</code> 型は条件式の評価や論理演算に広く使用され、プログラムの流れを制御するための基本的な手段として重要な役割を果たします。
===== 文字型(''The Character Type'') =====
Rustの文字型 <code>char</code> はUnicodeの単一の文字を表し、32ビットで符号化されます。Unicodeのサロゲートペアを含む広範な範囲の文字を表現できます。
以下のコードは、<code>char</code>型を使用してUnicode文字やサロゲートペアを扱う例です。
:<syntaxhighlight lang=rust copy>
fn main() {
// 単一のUnicode文字の表現
let unicode_char = '😊'; // 笑顔の絵文字 (U+1F60A)
println!("Unicode char: {}", unicode_char);
// サロゲートペアの表現
let surrogate_pair = '\u{1F601}'; // 涙の絵文字 (U+1F601)
println!("Surrogate pair: {}", surrogate_pair);
// char型のサイズを取得
println!("Size of char: {} bytes", std::mem::size_of::<char>());
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
Unicode char: 😊
Surrogate pair: 😁
Size of char: 4 bytes
</syntaxhighlight>
ここでは、<code>'😊'</code> という絵文字や <code>\u{1F601}</code> というサロゲートペアを <code>char</code>型として表現しています。絵文字やサロゲートペアも正しく表示されることを確認できます。また、<code>std::mem::size_of::<char>()</code> を使って <code>char</code>型のサイズを表示しています。
===== 文字列型(''The String Type'') =====
Rustには2つの主要な文字列型があります。
# <code>&str</code>型 (文字列スライス):
#* メモリ内のデータへの不変の参照を表します。
#* UTF-8でエンコードされた文字列を参照します。
#* 文字列リテラルや他のデータ構造の一部として使用されます。
#:<syntaxhighlight lang=rust copy>
let string_slice: &str = "Hello, Rust!"; // 文字列リテラルから作成された文字列スライス
</syntaxhighlight>
# <code>String</code>型:
#* ヒープ上に確保された可変の文字列データを持ちます。
#* 動的に変更可能で、文字列の追加や削除、変更が可能です。
#:<syntaxhighlight lang=rust copy>
let mut string_object = String::from("Hello"); // String型のインスタンスを生成
string_object.push_str(", Rust!"); // 文字列を追加
</syntaxhighlight>
これらの型は互いに相互変換できます。例えば、<code>&str</code>から<code>String</code>への変換は<code>to_string()</code>メソッドを使用できます。
:<syntaxhighlight lang=rust copy>
let my_string: String = "Hello".to_string(); // &strからStringへの変換
</syntaxhighlight>
また、<code>String</code>から<code>&str</code>への変換は、<code>&</code>演算子を使用して参照を取得します。
:<syntaxhighlight lang=rust copy>
let my_string: String = String::from("Hello");
let string_ref: &str = &my_string; // Stringから&strへの変換
</syntaxhighlight>
文字列操作に関しては、<code>String</code>型が動的に変更可能で柔軟性があり、<code>&str</code>型は主に静的な文字列の参照として使用されます。
;様々な文字列リテラル
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=9c41862df3833ee7bd16169631d74de7 string.rs]:<syntaxhighlight lang=rust copy>
fn main() {
println!("{:?}", "hello"); // エスケープされた文字列
println!("{:?}", r#"hello"#); // エスケープされないraw文字列
println!("{:?}", b"hello"); // エスケープされたバイト文字列
println!("{:?}", br#"hello"#); // エスケープされないrawバイト文字列
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
"hello"
"hello"
[104, 101, 108, 108, 111]
[104, 101, 108, 108, 111]
</syntaxhighlight>
* <code>"{:?}"</code> はデバッグ用のフォーマット指定子で、デバッグ表示用の形式で出力します。
* <code>r#"..."#</code> はエスケープされない raw 文字列リテラルで、内部のエスケープが無視されます。
* <code>b"..."</code> はバイト文字列リテラルで、ASCII文字のバイト値の配列を示します。
* <code>br#"..."#</code> はエスケープされない raw バイト文字列リテラルです。
これらのリテラルは、異なる用途で利用されることがあり、それぞれの特性や振る舞いが異なります。
===== ユニット型(''The Unit Type'') =====
Rustにおけるユニット型は<code>()</code>で表されます。ユニット型は特別な型であり、単一の値 <code>()</code> だけから成り立ちます。主に2つの用途があります:
# 関数の戻り値としての利用: 副作用のない関数や手続きにおいて、何も返す必要がない場合にユニット型 <code>()</code> が使用されます。
#:<syntaxhighlight lang=rust copy>
fn do_something() {
// 何らかの処理
}
fn main() {
let result = do_something(); // 戻り値は () になる
println!("result = {:?}", result);
}
</syntaxhighlight>
# 構造体のフィールドとしての利用: 構造体のフィールドとしてユニット型を持つことで、その構造体のインスタンスが存在することを示す場合に使用されます。
#:<syntaxhighlight lang=rust copy>
#[derive(Debug)]
struct MarkerUnit;
fn main() {
let marker = MarkerUnit; // ユニット型を持つ構造体のインスタンス化
println!("marker = {:?}", marker);
}
</syntaxhighlight>
ユニット型は一見すると何も持たない型ですが、プログラムの構造を表現するために重要な役割を果たしています。特に関数の戻り値として使用されることが多いです。
==== 複合型(''Compound Types'') ====
複合型(''Compound Types'')は、複数の値を1つの型にまとめることができます。
Rustにはタプルとアレイという2つのプリミティブな複合型があります。
===== タプル型(''The Tuple Type'') =====
タプル(''The Tuple'')は、さまざまな型の値を1つの複合型にまとめる一般的な方法です。
タプルの長さは固定されており、一度宣言すると大きくしたり小さくしたりすることはできません。
===== 配列型(''The Array Type'') =====
複数の値の集まりを持つもう一つの方法として、配列(''The Array'')があります。
タプルとは異なり、配列の各要素は同じ型でなければなりません。
Rustの配列は、タプルと同じく長さが固定されています。
== プレースホルダー ==
Rustの<code>println!</code>マクロなどの文字列表示マクロでは、文字列中の<code>{}</code>の位置に指定された書式に基づいて値が展開されます<ref>[https://doc.rust-lang.org/std/fmt/ Module std::fmt]</ref>。
=== フォーマッティング・トレイツ (Formatting traits) ===
Rustにおけるフォーマッティング・トレイツは、データを指定されたフォーマットで整形するためのトレイト(trait)です。例えば、空のプレースホルダー<code>{}</code>を指定すれば、その型に適した自然なフォーマット(例:100ならば "100")で表示できます。これには、[https://doc.rust-lang.org/std/fmt/trait.Display.html fmt::Display]トレイトが使用されます。
一方、基数を変更して16進数、8進数、2進数などの異なる表現で表示したい場合には、フォーマッティング・トレイツを活用します<ref>[https://doc.rust-lang.org/std/fmt/#formatting-traits Formatting traits]</ref>。
;[https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=3e0f75ec3db6eaa2f1db03cefefb5b44 Formatting traits]:<syntaxhighlight lang=rust copy>
fn main() {
println!("{:?}", 100); // Debug
println!("{:x}", 100); // LowerHex
println!("{:o}", 100); // Octal
println!("{:b}", 100); // Binary
println!("{}", 100); // Display
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
100
64
144
1100100
100
</syntaxhighlight>
フォーマッティング・トレイツは、Rustプログラミング言語でデータをフォーマットするためのメカニズムを提供します。これらのトレイツは、<code>std::fmt</code>モジュールに定義されており、さまざまなフォーマット方法を提供します。代表的なものは以下の通りです:
* '''Display''': <code>std::fmt::Display</code>トレイトは、<code>{}</code>を使用し、一般的に人間が読みやすい形式でデータを表示します。<code>println!</code>や<code>format!</code>マクロなどで使用されます。
* '''Debug''': <code>std::fmt::Debug</code>トレイトは、<code>{:?}</code>を使ってデバッグ目的で表示を行います。通常、開発中にデータの内容を調べるために使用されます。
* '''Binary''': <code>std::fmt::Binary</code>トレイトは、<code>{:#b}</code>でバイナリ形式での表示を提供します。
* '''Octal''': <code>std::fmt::Octal</code>トレイトは、<code>{:#o}</code>で8進数形式の表示を提供します。
* '''LowerHex / UpperHex''': <code>std::fmt::LowerHex</code>および<code>std::fmt::UpperHex</code>トレイトは、それぞれ<code>{:#x}</code>および<code>{:#X}</code>で16進数の小文字・大文字形式で表示します。
これらのトレイツは、カスタム型のデータを整形するために実装することができ、例えば<code>Display</code>トレイトを実装することで、カスタム型のデータを<code>{}</code>形式で表示できるようになります。
以下は、構造体<code>Person</code>に<code>Display</code>トレイトを実装し、<code>{}</code>を使用してカスタム型を表示する例です。
;[https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=1dc6f34658b858b48dad035ca7ef7117 カスタムDisplayトレイトの例]
:<syntaxhighlight lang=rust copy>
use std::fmt;
// 構造体を定義
struct Person {
name: String,
age: u32,
}
// Displayトレイトの実装
impl fmt::Display for Person {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// カスタムフォーマットを定義する
write!(f, "Name: {}, Age: {}", self.name, self.age)
}
}
fn main() {
let person = Person {
name: String::from("Alice"),
age: 30,
};
// {}を使ってPerson型を表示
println!("Person Details: {}", person);
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
Person Details: Name: Alice, Age: 30
</syntaxhighlight>
この例では、<code>Person</code>という構造体を定義し、<code>fmt::Display</code>トレイトを実装しています。<code>Formatter</code>型を使ってカスタムフォーマットを定義し、<code>write!</code>マクロを使って書式設定を行っています。<code>main</code>関数内で、<code>println!</code>マクロを使って<code>Person</code>型のインスタンスを表示し、<code>{}</code>を使用してカスタム型を整形する方法を示しています。
=== 浮動小数点数のフォーマット ===
浮動小数点数の表示には、<code>std::fmt::Float</code> トレイトを使います。以下の書式を使って、浮動小数点数の表現をフォーマットできます。
* '''e''': 指数形式(例:<code>1.23e4</code>)
* '''f''': 固定小数点形式(例:<code>123.456</code>)
* '''g''': 自動的に指数形式または固定小数点形式に切り替える(例:<code>1.23</code>)
* '''p''': 精度の高い16進数形式(例:<code>0x1.9p+1</code>)
例えば、以下のように浮動小数点数を表示することができます:
:<syntaxhighlight lang=rust copy>
let pi = 3.141592;
println!("{:.2}", pi); // 固定小数点形式で小数点以下2桁表示
println!("{:e}", pi); // 指数形式で表示
println!("{:g}", pi); // 自動的に形式を切り替える
println!("{:p}", pi); // 精度の高い16進数形式
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
3.14
3.141592e0
3.14159
0x1.921fb6p+1
</syntaxhighlight>
==== 浮動小数点の精度指定 ====
浮動小数点数の表示において、精度(小数点以下の桁数)を指定するには、書式の中で <code>.</code> の後に桁数を指定します(例:<code>{:.2f}</code>)。デフォルトでは、浮動小数点数の精度は6桁ですが、<code>{:.8e}</code> や <code>{:.12f}</code> で精度を変更することができます。
=== 文字列のフォーマット ===
文字列のフォーマットには、<code>std::fmt::Display</code> トレイトに基づいて、さまざまなオプションがあります。文字列に対しては、特に幅やアライメントを指定することができます。以下のような書式を使用できます:
* '''Width''': フィールドの幅を指定します。例えば、<code>{:10}</code> と書くと、最小で10文字分のスペースが確保され、足りない分は空白で埋められます。
* '''Alignment''': 左寄せ、右寄せ、中央寄せを指定できます。左寄せは <code><</code>、右寄せは <code>></code>、中央寄せは <code>^</code> を使います。
以下に文字列のフォーマット例を示します:
:<syntaxhighlight lang=rust copy>
let s = "Rust";
println!("{:>10}", s); // 右寄せで幅10
println!("{:<10}", s); // 左寄せで幅10
println!("{:^10}", s); // 中央寄せで幅10
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
Rust
Rust
Rust
</syntaxhighlight>
上記の例では、文字列 <code>"Rust"</code> に対して異なるアライメント(右寄せ、左寄せ、中央寄せ)を指定しています。
== 所有権システム ==
Rustの所有権システムは、メモリ管理とリソースの安全な扱いを通じて安全性と並行性を確保する、言語の重要な特徴です。ここでは、Rustの所有権、借用、参照について、コードを交えて解説します。
=== 所有概念 ===
Rustの所有概念は、変数がリソースの所有権を持ち、スコープを抜ける際にそのリソースを自動的に解放する仕組みです。この仕組みは、メモリ安全性を高めるための中心的な考え方です。
=== コピーとムーブ ===
Rustでは、変数が参照しているデータがスタック上かヒープ上かによって、コピーやムーブの挙動が異なります。スタック上のプリミティブ型はコピーされますが、ヒープ上のデータはムーブされ、所有権が移動します。
; コピーされるデータ
:<syntaxhighlight lang=rust line copy>
fn main() {
let x = 5;
let y = x; // xの値がコピーされる
println!("x: {}, y: {}", x, y); // 両方の変数が利用可能
}
</syntaxhighlight>
この例では、整数型<code>x</code>の値がコピーされるため、<code>x</code>と<code>y</code>の両方が使用できます。
; ムーブされるデータ
:<syntaxhighlight lang=rust line copy>
fn main() {
let s1 = String::from("Hello");
let s2 = s1; // s1の所有権がs2に移動
// println!("{}", s1); // コンパイルエラー:s1はもう使用できない
println!("{}", s2); // 正常に出力される
}
</syntaxhighlight>
ここでは、<code>s1</code>の所有権が<code>s2</code>に移動するため、<code>s1</code>は利用できなくなります。
次に、コピーとムーブの違いを簡単に表にまとめます。
:{| class=wikitable
|+ コピーとムーブの違い
! データ種別!! コピー !! ムーブ
|-
! プリミティブ型(整数型、bool型など)
| コピーされる
| コピーされる
|-
! ヒープ上のデータ(String型、Vec型など)
| されない
| 所有権が移動する
|}
=== 借用と参照 ===
所有権を移動せずにデータを利用する方法として、借用(参照)があります。
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=91624181f640bfff93914d318547bfae 借用と参照の例]
:<syntaxhighlight lang=rust line copy>
fn main() {
let s1 = String::from("Hello");
// s1を借用する(イミュータブルな参照)
let len = calculate_length(&s1);
println!("The length of '{}' is {}.", s1, len); // 正常に出力される
}
fn calculate_length(s: &String) -> usize {
s.len()
}
</syntaxhighlight>
この例では、<code>calculate_length</code>関数が<code>&String</code>型のイミュータブルな参照を受け取っているため、<code>s1</code>の所有権を渡すことなく値を参照できます。
=== ミュータブルな参照 ===
可変の値を変更するには、ミュータブルな参照を使います。
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=1ef888bfe58461858b4cefe84d3b6fae ミュータブルな参照の例]
:<syntaxhighlight lang=rust line copy>
fn main() {
let mut s = String::from("Hello");
change_string(&mut s);
println!("{}", s); // "Hello, goodbye"が出力される
}
fn change_string(s: &mut String) {
s.push_str(", goodbye");
}
</syntaxhighlight>
<code>&mut String</code>型のミュータブルな参照を利用して<code>change_string</code>関数で文字列を変更しています。
=== 関数のパラメータにした変数は関数にムーブされる ===
Rustでは、関数の引数として変数を渡すと、所有権が関数にムーブされる場合があります。例で見てみましょう。
:<syntaxhighlight lang=rust copy>
fn main() {
let original = String::from("Hello, Rust!"); // String型の変数originalを作成
let moved = move_ownership(original); // originalがmove_ownership関数にムーブ
// println!("original: {}", original); // コンパイルエラー
println!("moved: {}", moved); // 正常に動作
}
fn move_ownership(input: String) -> String {
input
}
</syntaxhighlight>
この例では、<code>original</code>が関数<code>move_ownership</code>にムーブされ、<code>original</code>は使用できなくなります。
=== クローン ===
所有権を共有したい場合、<code>clone</code>メソッドでデータの複製が可能です。
:<syntaxhighlight lang=rust copy>
fn main() {
let data1 = vec![1, 2, 3];
let data2 = data1.clone(); // データの複製
println!("{:?}", data1); // data1は所有権を保持している
println!("{:?}", data2); // data2はクローンされたデータを持つ
}
</syntaxhighlight>
この例では、<code>data1</code> の <code>clone</code> メソッドを呼び出すことで、ヒープ上にあるデータの複製を作成し、<code>data2</code> に格納します。このとき、<code>data1</code> と <code>data2</code> はそれぞれ独立した所有権を持つため、どちらかを変更してももう一方には影響しません。
クローンを作成することで、所有権が必要な場合でも、元のデータをそのまま保持しながらコピーを生成できるため、データの安全な扱いが可能です。
=== ライフタイム ===
ライフタイムは、参照が有効である期間を示すRustの注釈で、所有権と借用のルールと密接に関係しています。ライフタイムを指定することで、コンパイラがメモリの安全性を保証し、不正なメモリアクセスや参照の無効化を防ぎます。
:<syntaxhighlight lang=rust copy>
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
</syntaxhighlight>
この例では、<code>'a</code> というライフタイムパラメータを使って、<code>x</code> と <code>y</code> の参照が同じライフタイムを共有することをコンパイラに伝えています。これにより、返り値のライフタイムも入力引数と同じ期間で有効であることが保証されます。
ライフタイムは複雑な場面では特に重要となり、Rustの所有権システムがさらに強力にメモリ安全を保証する仕組みとなっています。
=== Rc, Box と Drop ===
==== Rc (Reference Counting) ====
<code>Rc</code>は参照カウントを用いたスマートポインタで、複数の所有者が同じデータを共有できるようにします。主にマルチスレッドを必要としない環境で利用され、<code>clone</code>メソッドでカウントを増加させることで、同じデータを複数の変数が所有できます。
:<syntaxhighlight lang=rust copy>
use std::rc::Rc;
let a = Rc::new(5);
let b = Rc::clone(&a); // aとbが同じデータを共有
</syntaxhighlight>
<code>Rc</code>はスレッドセーフではありませんが、シングルスレッドの環境でデータ共有を簡単に行えるため便利です。
==== Box (Heap Allocation) ====
<code>Box</code>はデータをヒープに格納し、そのポインタをスタック上で保持するスマートポインタです。スタックではなくヒープにデータを配置したい場合に使用します。<code>Box</code>は最も単純なスマートポインタで、シングルオーナーのデータの所有権を提供します。
:<syntaxhighlight lang=rust copy>
let b = Box::new(5);
println!("b = {}", b); // ヒープ上に格納されたデータにアクセス
</syntaxhighlight>
ボックスは特に再帰的なデータ構造の作成やヒープメモリ管理に適しており、ポインタの解放はスコープを外れたときに自動で行われます。
==== Drop (Resource Cleanup) ====
<code>Drop</code>トレイトは、オブジェクトがスコープを外れるときにリソースをクリーンアップするための機構を提供します。カスタムデストラクタを実装する際に使用され、例えばファイルハンドルやネットワークリソースなどのリソース解放に役立ちます。
:<syntaxhighlight lang=rust copy>
struct CustomResource;
impl Drop for CustomResource {
fn drop(&mut self) {
println!("CustomResourceが解放されました");
}
}
let resource = CustomResource;
// スコープを抜けるときにdropメソッドが呼ばれる
</syntaxhighlight>
<code>Drop</code>トレイトにより、Rustは自動的にリソース管理を行い、メモリリークやリソースの取りこぼしを防ぎます。手動で<code>drop</code>を呼び出すことも可能ですが、通常はスコープ終了時に自動で解放されます。
; まとめ
Rustの所有権システムは、メモリ管理とデータ競合の予防において非常に強力な機能です。所有、借用、ムーブ、コピー、クローンといった各機能を活用することで、安全かつ効率的にメモリを管理できるようになります。特に、Rustではコンパイル時にこれらのルールが強制されるため、意図しないバグを未然に防ぐことができます。
Rustの所有権システムを深く理解することは、効率的で安全なコードを書くための第一歩です。<!ーー
RcやBoxやDropもここか?
ーー>
== 制御構造 ==
Rust では、{{code|if}}や{{code|for}}などの制御構造も式です。
=== 分岐 ===
Rust は、[[#if|if]] と [[#match|match]] の2つの分岐構文を持ちます。
==== if ====
ifは、条件式に基づき分岐し、分岐先の式を評価します。
ifの値は、分岐先の式の値です。
elseを省略し条件式が偽であった場合のifの値は <code>()</code> です。
;構文:<syntaxhighlight lang=ebnf>
if-expr := if 条件式 '{' 式1 '}' [ else '{' 式2 ] '}'
</syntaxhighlight>
:;[https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=84117443c135c52137b9005717322e82 条件式に整数を使うと]:<syntaxhighlight lang=Rust line copy>
fn main() {
let i = 0;
if i {
println!("zero");
}
}
</syntaxhighlight>
:;コンパイルエラー:<syntaxhighlight lang=text>
error[E0308]: mismatched types
--> src/main.rs:4:8
|
4 | if i {
| ^ expected `bool`, found integer
For more information about this error, try `rustc --explain E0308`.
</syntaxhighlight>
:: Rustでは、ifに限らず、条件式は、bool 型でなければいけません。
;[https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=2c8960df447a87aaeaecced963d20cde if.rs]:<syntaxhighlight lang=rust line copy>
fn main() {
let i = 0;
if i == 0 {
println!("零");
} else {
println!("非零");
}
let s = if i == 0 {
"ゼロ"
} else {
"非ゼロ"
};
println!("{}", s);
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
零
ゼロ
</syntaxhighlight>
:気をつけたいのは、式の値が参照されるifでは、それぞれの式(ここでは <code>"ゼロ"</code> と <code>"非ゼロ"</code>)にセミコロン<code> ; </code>を付けてはいけない点です。
:: もし セミコロン<code> ; </code> をつけると、ブロックの値は、Unit 型 <code>()</code> になります。
:: ifの、分岐先の2つの式の型は同じでなければいけません。
:: else節が省略された場合は、Unit 型を返さなければいけません。
::: 式の最後に、セミコロン<code>};</code> が必要ということです<ref>C言語系では、式を文にする為いに<code>};</code> が必要です。Rustもそう説明されている場合がありますが、Rustでは式の型の一致が目的です。</ref>。
:また、<syntaxhighlight lang=rust inline>let s = if i == 0 {</syntaxhighlight>の文末の<code>};</code> のセミコロンも忘れがちです。
:Rust では if に限らず、コードブロックは式の一種で値を返します<ref>コードブロックが値を持つプログラミング言語としては、BCPL, [[Ruby]], [[Scala]]や[[Kotlin]]があります。</ref>。その場合、コードブロックの最後の式がコードブロックの値となりセミコロン<code> ; </code>は不要で、もし、<code> ; </code>をつけると<code> ; </code>の次の式(=空文)の値<code> () </code>がコードブロックの値になります。
この特徴は、関数型プログラミングを意識したものですが、同時に後に説明する所有権の移譲で重要な役割を果たします。
===== if と else の連鎖 =====
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=e3c9a903bc6baab687f697a83bde8928 if-else.rs]:<syntaxhighlight lang=rust line copy>
fn main() {
let n = 0.0 / 0.0;
if n < 0.0 {
println!("負の数");
} else if n > 0.0 {
println!("正の数");
} else if n == 0.0 {
println!("零");
} else {
println!("{n}");
}
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
NaN
</syntaxhighlight>
このコードの中で、次のことが行われています:
# <code>n</code> に NaN(Not a Number)を代入しています。<code>0.0 / 0.0</code> は浮動小数点数のゼロで割り算を表し、NaN を返します。
# <code>if</code> 文で <code>n</code> の値に応じて条件分岐しています。
# <code>n</code> が負の数より小さい場合は、"負の数" と出力します。
# <code>n</code> が正の数より大きい場合は、"正の数" と出力します。
# <code>n</code> がゼロと等しい場合は、"零" と出力します。
# それ以外の場合は、<code>n</code> の値を <code>{n}</code> という形式で出力します。
しかし、Rustの浮動小数点数型では、NaN は <code><</code> や <code>></code> 、<code>==</code> などの比較演算子によって、他の数値との大小や等価性を比較することはできません。なぜなら NaN は "not a number" であり、数値としての大小関係が定義されていないためです。
そのため、このコードでは <code>n</code> が NaN である場合、どの条件にも合致せず、最後の <code>else</code> ブロックが実行されることになります。このブロックでは、<code>n</code> の値そのものを <code>{n}</code> という形式で出力しようとしています。
==== Some() ====
Rustでは、C言語のNULLに相当する値は None です。
通常の変数は None の代入は受付けませんが、Some() を使うと None を取り得る変数が宣言できます<ref>[https://doc.rust-lang.org/stable/std/option/enum.Option.html Option in std::option - Rust]</ref>。
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=afb9193c50a316090b6e3119f994f8cf some.rs]:<syntaxhighlight lang=rust line copy>
fn main() {
let mut x = Some(0);
println!("{:?}({})", x, type_of(x));
x = None;
println!("{:?}({})", x, type_of(x));
}
fn type_of<T>(_: T) -> &'static str {
std::any::type_name::<T>()
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
Some(0)(core::option::Option<i32>)
None(core::option::Option<i32>)
</syntaxhighlight>
Some() 及び None は、標準ライブラリ std::option で定義されていますが、初期化済みなので Use することなく、接頭辞Option::なしで使えます。
==== if let ====
Rustには、<code>if let</code>という特別な制御構造があります。<code>if let</code>は、<code>match</code>式を簡略化するために使用されます。<code>if let</code>式は、単一のパターンを使用して、変数が指定された値にマッチした場合にのみ式を実行します。<code>if let</code>を使用すると、冗長なマッチングのコードを削減できます。
以下は、<code>if let</code>を使用した例です。
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=0614af1127dbc6ec0d871443b16e6a67 if-let.rs]:<syntaxhighlight lang=rust style="width:40em">
fn main() {
let mut v = vec![2, 3, 5];
if let Some(x) = v.pop() {
println!("x = {}", x)
} else {
println!("done!")
}
if let Some(x) = v.pop() {
println!("x = {}", x)
} else {
println!("done!")
}
if let Some(x) = v.pop() {
println!("x = {}", x)
} else {
println!("done!")
}
if let Some(x) = v.pop() {
println!("x = {}", x)
} else {
println!("done!")
}
if let Some(x) = v.pop() {
println!("x = {}", x)
} else {
println!("done!")
}
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text style="width:40em">
x = 5
x = 3
x = 2
done!
done!
</syntaxhighlight>
: <code>.pop()</code>メソッドは、ベクターから最後の要素を取り出し、それを返します。ただし、ベクターが空である場合は<code>None</code>を返します。
: 最初の行では、<code>vec![2, 3, 5]</code>という3つの要素を持つベクターを作成しています。
: その後、5回の<code>if let</code>式を使用して、ベクターから要素を取り出し、それを表示するか、ベクターが空の場合には<code>done!</code>と表示します。
: 各<code>if let</code>式では、変数<code>x</code>を定義しています。
:: <code>v.pop()</code>の返り値が<code>Some</code>であれば、ベクターの最後の要素が変数<code>x</code>に束縛され、<code>println!</code>マクロを使用して<code>x</code>を表示します。
:: そうでない場合、すなわち、ベクターが空である場合は、<code>else</code>節が実行されて、<code>done!</code>が表示されます。
==== match ====
<code>match</code>は、値とパターンをマッチングして、対応するコードブロックを実行します。これは、複数の条件に基づいて処理を行う場合に非常に便利です。例えば、列挙型(enum)を使った状況では、<code>match</code>は特に有用です。
:<syntaxhighlight lang=rust copy>
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> u32 {
match coin {
Coin::Penny => {
println!("Lucky penny!");
1
},
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
</syntaxhighlight>
上記の例では、<code>Coin</code>型の列挙子を受け取り、それぞれのコインに対する価値を返す関数が定義されています。<code>match</code>は<code>Coin</code>の値を取り、各列挙子に対応するコードブロックを実行します。例えば、<code>Coin::Penny</code>の場合、"Lucky penny!"というメッセージが表示され、1が返されます。
<code>match</code>は、パターンに加えて<code>if</code>条件も組み合わせて使用できます。
:<syntaxhighlight lang=rust copy>
fn main() {
let some_value = Some(5);
match some_value {
Some(x) if x < 5 => println!("Less than 5: {}", x),
Some(x) => println!("Value: {}", x),
None => println!("No value"),
}
}
</syntaxhighlight>
この例では、<code>Some</code>型の値を持つ変数を<code>match</code>で処理しています。<code>if</code>条件は<code>Some</code>型であり、かつその値が5未満の場合にマッチします。それ以外の場合、単に値を表示します。
<code>match</code>はRustの強力なツールであり、パターンマッチングにより、安全性と表現力を向上させます。
=== 反復 ===
Rustには、以下のような反復構文があります。
# <code>[[#for|for]]</code>ループ
## <code>[[#iter()|iter()]]</code>メソッドを使用した反復処理
## <code>[[#enumerate()|enumerate()]]</code>メソッドを使用した反復処理
## <code>[[#zip()|zip()]]</code>メソッドを使用した反復処理
# <code>[[#while|while]]</code>ループ
# <code>[[#loop|loop]]</code>ループ
それぞれの構文について、詳しく解説していきます。
==== for ====
Rust の '''for''' は、指定された範囲内の値を反復処理するために使用されます。通常、配列、ベクトル、範囲、またはイテレータなどの反復可能オブジェクトに対して使用されます。
;構文:<syntaxhighlight lang=ebnf>
for_expression = "for" loop_variable "in" expression "{" statement* "}";
loop_variable = pattern;
expression = (expression_binop | expression_unop | expression) ;
pattern = identifier | "_" | literal ;
</syntaxhighlight>
: <code>for_expression</code>はforループを表し、 <code>loop_variable</code>は反復処理のために使用される変数、 <code>expression</code>は反復処理の対象となるデータのソース、 <code>statement</code>はループ内で実行される文を表します。 <code>pattern</code>は、ループ変数の型と一致するパターンを表します。
: <code>identifier</code>は、識別子の名前を表します。 <code>literal</code>は、文字列、数値、真偽値などのリテラル値を表します。
: forループは、 <code>loop_variable</code>によって定義された変数に <code>expression</code>で指定されたデータソースの値を順番に割り当て、それぞれの値に対して <code>statement</code>を実行します。
: <code>identifier</code>は識別子を、<code>literal</code>はリテラルをしめします。
===== Range =====
Rustにおける<code>range</code>は、範囲を表す型です。範囲の生成には2つの主要な方法があります。
;..(半開区間)
* <code>start..end</code>の形式で使用され、<code>start</code>から<code>end</code>の手前(<code>end</code>は含まれない)までの範囲を生成します。
例えば、<code>1..5</code>は1から4までの範囲を生成します。
; ..= (閉区間)
* <code>start..=end</code>の形式で使用され、<code>start</code>から<code>end</code>までの閉区間を生成します(<code>end</code>も含まれます)。
例えば、<code>1..=5</code>は1から5までの範囲を生成します。
これらの範囲は<code>for</code>ループの反復やイテレータの作成など、さまざまな場面で使用されます。例えば、<code>for</code>ループやイテレータを使って範囲内の要素を処理することができます。
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=a3cd190fbd860ce8efb0d6276fb0858a for-in-range.rs]:<syntaxhighlight lang=rust copy>
fn main() {
// 半開区間の使用例
for i in 1..5 {
println!("{i}"); // 1, 2, 3, 4が出力される
}
// 閉区間の使用例
for i in 1..=5 {
println!("{i}"); // 1, 2, 3, 4, 5が出力される
}
}
</syntaxhighlight>
範囲は整数や文字など、多くの型で使用できます。範囲の使用はイテレーションや特定の範囲内の操作を行う際に便利です。
===== iter() =====
<code>Rust</code>における<code>iter()</code>は、コレクション(ベクター、配列、ハッシュマップなど)をイテレート可能な形に変換するメソッドです。イテレータは、コレクション内の要素を1つずつ処理するための仕組みを提供します。
基本的な使い方は以下のようになります:
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=d45c8c0ad55adb01e50456dd7270983c iter.rs]:<syntaxhighlight lang=rust copy>
fn main() {
let vec = vec![1, 2, 3, 4, 5];
// ベクターのイテレータを作成する
let mut iter = vec.iter();
// イテレータを使って要素に順番にアクセスする
while let Some(value) = iter.next() {
println!("{value}");
}
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
1
2
3
4
5
</syntaxhighlight>
<code>iter()</code>メソッドは、イテレータを作成するための最も基本的な手段ですが、さまざまな応用もあります。
# <code>.map()</code>: イテレータを他の形に変換する場合に使われます。たとえば、各要素に対して関数を適用して新しいイテレータを作成します。
#:<syntaxhighlight lang=rust copy>
fn main() {
let vec = vec![1, 2, 3, 4, 5];
// 各要素を2倍する新しいイテレータを作成する
let doubled_iter = vec.iter().map(|x| x * 2);
for value in doubled_iter {
println!("{value}");
}
}
</syntaxhighlight>
# <code>.filter()</code>: 条件に一致する要素のみを含む新しいイテレータを作成します。
#:<syntaxhighlight lang=rust copy>
fn main() {
let vec = vec![1, 2, 3, 4, 5];
// 偶数の要素だけを含む新しいイテレータを作成する
let even_iter = vec.iter().filter(|&x| x % 2 == 0);
for value in even_iter {
println!("{value}");
}
}
</syntaxhighlight>
# <code>.fold()</code>: イテレータ内の要素を畳み込んで単一の値に集約します。
#:<syntaxhighlight lang=rust copy>
fn main() {
let vec = vec![1, 2, 3, 4, 5];
// 要素の合計を計算する
let sum = vec.iter().fold(0, |acc, &x| acc + x);
println!("合計: {sum}");
}
</syntaxhighlight>
# <code>.reduce()</code>: イテレータ内の要素を畳み込んで単一の値に集約します。
#:<syntaxhighlight lang=rust copy>
fn main() {
let vec = vec![1, 2, 3, 4, 5];
// 要素の合計を計算する
let sum = vec.into_iter().reduce(|acc, x| acc + x);
println!("合計: {:?}", sum);
}
</syntaxhighlight>
これらは<code>iter()</code>を基盤として利用する機能の一部です。<code>Rust</code>のイテレータは非常に強力で、関数型プログラミングの概念を取り入れながら、効率的で安全なコードを記述するための重要な手段となっています。
====== rev() ======
<code>iter()</code>メソッドに加えて、<code>rev()</code>メソッドを使用すると、要素を逆順で取り出すこともできます。
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=83d50bb12645221a766052413a9d73b8 iter-rev.rs]:<syntaxhighlight lang=rust copy>
fn main() {
let v = vec![1, 3, 5, 7, 11];
for x in v.iter().rev() {
println!("x = {}", x)
}
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
x = 11
x = 7
x = 5
x = 3
x = 1
</syntaxhighlight>
: このRustのコードは、 <code>for</code>ループを使用して、ベクトル <code>v</code>の要素を反復処理し、各要素の値を出力するものです。ただし、 <code>v</code>の要素を逆順に処理します。
: <code>v.iter().rev()</code>は、 <code>v</code>のイテレータを取得し、そのイテレータを逆順にするためのメソッドです。これにより、ベクトルの最後の要素から始まり、最初の要素まで逆順に反復処理します。
: <code>for</code>ループの本体では、 <code>println!</code>マクロを使用して、現在の <code>x</code>の値を表示しています。 <code>{}</code>はプレースホルダーであり、その場所に変数の値が挿入されます。この例では、 <code>{}</code>の中に <code>x</code>を指定して、ループの反復ごとに変化する <code>x</code>の値を表示しています。
===== enumerate() =====
<code>enumerate()</code>メソッドを使用すると、要素のインデックスと値を同時に取り出すこともできます。
;コード例:<syntaxhighlight lang=rust line copy>
let v = vec![1, 2, 3];
for (i, val) in v.iter().enumerate() {
println!("{}: {}", i, val);
}
</syntaxhighlight>
: このコードでは、<code>v.iter().enumerate()</code>メソッドを使用して、<code>v</code>のイテレータを作成し、各要素のインデックスと値を同時に反復処理しています。<code>for</code>ループの本体では、変数<code>i</code>を使ってインデックス、<code>val</code>を使って値を表示しています。
===== zip() =====
<code>zip()</code>メソッドを使用すると、複数のイテレータを同時に取り出すことができます。
;コード例:<syntaxhighlight lang=rust line copy>
let v1 = vec![1, 2, 3];
let v2 = vec!["one", "two", "three"];
for (i, val) in v1.iter().zip(v2.iter()) {
println!("{}: {}", i, val);
}
</syntaxhighlight>
: このコードは、<code>zip()</code>メソッドを使用して、2つのベクトルを同時に反復処理する方法を示しています。
: 最初の行で、整数のベクトル<code>v1</code>と文字列のベクトル<code>v2</code>を定義しています。
: 次に、<code>for</code>ループを使用して、<code>v1.iter()</code>と<code>v2.iter()</code>のイテレータを同時に取り出します。このとき、<code>(i, val)</code>というタプルの形式で、それぞれのイテレータから次の要素を取り出します。
: <code>println!</code>マクロの中で、<code>{}</code>に<code>i</code>と<code>val</code>をそれぞれ表示しています。<code>{}</code>の中にある<code>:</code>は区切り文字で、<code>i</code>と<code>val</code>を区別するために使用されています。
==== while ====
Rustにおいて、<code>while</code>は指定した条件式が<code>true</code>である限り、ブロック内のコードを繰返し実行する制御構文です。
;[https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=47dc5b7c9d866b3f5913a77b9a55988e while.rs]:<syntaxhighlight lang=rust copy>
fn main() {
let mut i = 0;
while i < 5 {
println!("i = {}", i);
i += 1
};
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
i = 0
i = 1
i = 2
i = 3
i = 4
</syntaxhighlight>
: このプログラムは、0から4までの数字を順番に表示するプログラムです。whileループを使用して、条件式<code>i < 5</code>が<code>true</code>である間、ループを継続します。ループの各イテレーションでは、<code>println!</code>マクロを使用して、変数<code>i</code>の現在の値を表示します。<code>i</code>の値は、ループ本文の最後で<code>i += 1</code>によって1ずつ増加します。条件式<code>i < 5</code>が<code>false</code>になったとき、ループが終了します。最終的に、0から4までの数字が順番に表示されます。
===== while let =====
Rustの<code>while let</code>は、ループ処理の一種で、パターンマッチングを行い、パターンにマッチする値をループ内で取り出しながらループを繰り返します。
<code>while let</code>は、<code>match</code>構文の糖衣構文で、一つの値を取り出してパターンマッチングを行い、パターンにマッチする場合は値を取り出し、マッチしない場合はループを終了します。
;[https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=0764d8389e3afc0854b4223b8db5a32b while-let.rs]:<syntaxhighlight lang=rust copy>
fn main() {
let mut n = Some(0);
while let Some(i) = n {
n = if i > 5 {
None
} else {
println!("i = {}", i);
Some(i + 1)
}
}
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
</syntaxhighlight>
: このコードは、<code>n</code> という <code>Option<i32></code> 型の変数を定義し、初期値を <code>Some(0)</code> として設定します。そして、<code>while</code> ループを使って <code>n</code> の値を取り出しながら、それが <code>None</code> でなければループを続けます。
: <code>while let</code> の条件式では、<code>n</code> が <code>Some(i)</code> とパターンマッチされ、<code>i</code> に値がバインディングされます。このパターンマッチにより、<code>n</code> が <code>Some(i)</code> でなければループは終了します。
: ループ本体では、<code>i</code> が <code>5</code> を超える場合は、<code>n</code> を <code>None</code> に更新してループを終了します。そうでなければ、<code>i</code> を表示して、<code>Some(i + 1)</code> を <code>n</code> に代入してループを継続します。
: このプログラムの出力は、0 から 5 までの数値が順番に表示されます。最後に <code>None</code> が表示されます。
===== while let とベクトル =====
Rustの<code>while let</code>は、反復可能な値に対して、パターンマッチングを行いながら反復処理を行うことも出来ます。ベクトルに対してwhile letを使うと、ベクトルの末尾から要素を取り出しながら反復処理を行うことができます。
;[https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=6b252d13f67df88f533e55dab5d3397c while-let-again.rs]:<syntaxhighlight lang=rust copy>
fn main() {
let mut v = vec![1, 3, 5, 7, 11];
while let Some(x) = v.pop() {
println!("x = {}", x)
}
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
x = 11
x = 7
x = 5
x = 3
x = 1
</syntaxhighlight>
: このコードは、可変長配列<code>v</code>に値を追加し、whileループでその配列から値を取り出し、それを表示するものです。
: まず、可変長配列<code>v</code>に<code>vec![1, 3, 5, 7, 11]</code>という値を設定しています。その後、<code>while let</code>式を使って、<code>v.pop()</code>の戻り値が<code>Some(x)</code>である限り、ループが継続されます。<code>v.pop()</code>は、<code>v</code>の最後の要素を取り出し、その要素があれば<code>Some</code>で包んで返し、なければ<code>None</code>を返します。
: <code>while let</code>式では、取り出した値が<code>Some(x)</code>である場合に、<code>println!("x = {}", x)</code>を実行して<code>x</code>の値を表示します。
: つまり、このコードは<code>v</code>の末尾から要素を取り出しながら、取り出した要素の値を表示するという処理を続け、最後に配列が空になったらループを終了します。
{{コラム|Rustにdo-whileはありません|2=
Rustにdo-whileはありません。while の条件式に do のブロックを詰め込むことで同じことが実現できます。
;[https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=534d7cfaf805c7e8d99d4c1ba95ed3ab pseudo-do-while.rs]:<syntaxhighlight lang=rust highlight=6 line copy>
fn main() {
let mut i = 100;
while {
println!("{}", i);
i += 1;
i < 10
} {}
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
100
</syntaxhighlight>
:whileの条件式が省略されたかのように見えますが、4,5,6行目を含むブロックが、while の条件式となり値はブロックの最後の6行目の式の値です。
:7行目の {} がループ本体です。
;loopを使った例:<syntaxhighlight lang=rust line copy>
fn main() {
let mut i = 100;
loop {
println!("{}", i);
i += 1;
if !(i < 10) {
break;
}
}
}
</syntaxhighlight>
: loop のブロックの最後に脱出条件を書いた方がわかりやすいかもしれません。
}}
==== loop ====
<code>loop</code>は、明示的な条件式の指定がない限り、無限に繰り返されます。
;[https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=9349b3eff79e311c70c87d20942cf8f2 loop-and-break.rs]:<syntaxhighlight lang=rust copy>
fn main() {
let mut i = 0;
let result = loop {
if i > 3 {
break 100;
}
println!("i = {}", i);
i += 1;
};
println!("result = {}", result);
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
i = 0
i = 1
i = 2
i = 3
result = 100
</syntaxhighlight>
: 最初に、変数<code>i</code>を0に初期化します。そして、<code>loop</code>構文で、<code>i</code>が3より大きくなるまで繰り返します。
: 各反復中に、<code>if</code>文が使用されています。<code>if</code>の条件式は、<code>i</code>が3より大きくなった場合には<code>break 100;</code>が実行され、<code>loop</code>から脱出します。そうでない場合は、<code>println!()</code>関数を使用して、<code>i</code>の値を出力し、<code>i</code>を1増やします。
: 最後に、<code>loop</code>から脱出した後に<code>result</code>変数に格納される値は、<code>break</code>文の引数で指定された<code>100</code>です。そして、<code>println!()</code>関数を使用して、<code>result</code>の値を出力します。
: つまり、このコードは、<code>i</code>を0から3まで順に増やしながら、各値を出力し、<code>i</code>が3より大きくなったら100を返し、その後に<code>result</code>の値を出力するという処理を行っています。
==== continue ====
<code>continue</code> を実行すると、ループのブロック内の残りの処理がスキップされ、反復処理が続行されます。
;[https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=49f7412cec92162b9d77881e08f43138 continue.rs]:<syntaxhighlight lang=rust highlight=9 line copy>
fn main() {
let mut i = 0;
let result = loop {
if i > 10 {
break 100;
}
if i % 2 == 0 {
i += 1;
continue;
}
println!("i = {}", i);
i += 1;
};
println!("result = {}", result);
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
i = 1
i = 3
i = 5
i = 7
i = 9
result = 100
</syntaxhighlight>
: このコードは、0から10までの奇数を出力するプログラムです。
: まず、<code>i</code>変数を定義して0で初期化し、<code>result</code>変数を宣言しています。次に、<code>loop</code>キーワードでループを開始します。
: ループ本体では、<code>if</code>文を使って<code>i</code>が10より大きくなったら、<code>break</code>キーワードを使ってループを抜けます。これにより、11以上の奇数が出力されることはありません。
: 次に、<code>i</code>が偶数である場合は、<code>continue</code>キーワードを使ってループの先頭に戻ります。これにより、偶数はスキップされます。
: 最後に、<code>println!</code>マクロを使って現在の<code>i</code>の値を出力し、<code>i</code>を1増やします。
:ループが終了したら、最後に<code>result</code>の値を出力します。<code>break</code>キーワードが値を返すため、このループは<code>result</code>に100を設定します。
{{コラム|width=stretch|Rustのプログラミングでつまづきやすいところ|2=Rustはセーフティ、パフォーマンス、そしてエクスプレッションの柔軟性を組み合わせた高度なプログラミング言語です。初めてRustを使う場合、以下のようなトピックでつまづくことがよくあります。
#所有権と借用: Rustでは、各値には所有者があり、値のライフサイクルを追跡する必要があります。また、他のコードに値を渡すときは、その値を一時的に借りる必要があります。これらの概念が理解しづらい場合があります。
#ライフタイム: Rustでは、各値にはライフタイムがあり、その値が使用される期間を決定します。ライフタイムの概念が不明瞭になりがちで、特に複雑なデータ構造を扱う場合に問題が発生することがあります。
#パフォーマンスの最適化: Rustは高速な実行時パフォーマンスを提供しますが、それを達成するためには、手動でメモリ管理や最適化を行う必要があります。これらの最適化は、Rustの型システムと他の言語との違いを理解する必要があります。
#コンパイラエラー: Rustのコンパイラは非常に厳格で、コードがコンパイルできない場合があります。エラーメッセージは詳細で役に立ちますが、初めて見た場合は驚くかもしれません。
本書では、これらのトピックについて順次取り上げてゆきます。
}}
== 演算子 ==
Rustでは、演算子を利用して変数やリテラルの値を操作することができます。これらは算術計算、比較、論理操作、ビット操作など、さまざまな目的に応じて使用されます。Rustの演算子は、型安全性と明確性を重視して設計されており、他のプログラミング言語と似た点も多いですが、いくつかの独自の特性も持っています。
=== 算術演算子 ===
算術演算子は、数値型に対する基本的な計算を行います。これには以下が含まれます。
* <code>+</code>(加算):2つの値を加算します。
* <code>-</code>(減算):1つ目の値から2つ目の値を減算します。
* <code>*</code>(乗算):2つの値を掛け算します。
* <code>/</code>(除算):1つ目の値を2つ目の値で割ります。ただし、整数型の場合、結果は切り捨てられます。
* <code>%</code>(剰余):1つ目の値を2つ目の値で割った余りを求めます。
例:
:<syntaxhighlight lang=rust copy>
let a = 10;
let b = 3;
println!("加算: {}", a + b); // 出力: 加算: 13
println!("除算: {}", a / b); // 出力: 除算: 3
println!("剰余: {}", a % b); // 出力: 剰余: 1
</syntaxhighlight>
=== 比較演算子 ===
比較演算子は、値同士を比較し、<code>true</code>または<code>false</code>を返します。
* <code>==</code>(等価):2つの値が等しい場合に<code>true</code>。
* <code>!=</code>(非等価):2つの値が等しくない場合に<code>true</code>。
* <code><</code>(小なり):左の値が右の値より小さい場合に<code>true</code>。
* <code>></code>(大なり):左の値が右の値より大きい場合に<code>true</code>。
* <code><=</code>(小なりイコール):左の値が右の値以下の場合に<code>true</code>。
* <code>>=</code>(大なりイコール):左の値が右の値以上の場合に<code>true</code>。
例:
:<syntaxhighlight lang=rust copy>
let x = 5;
let y = 10;
println!("x < y: {}", x < y); // 出力: x < y: true
println!("x == y: {}", x == y); // 出力: x == y: false
</syntaxhighlight>
=== 論理演算子 ===
論理演算子は、真理値を操作します。
* <code>&&</code>(論理AND):両方が<code>true</code>のときに<code>true</code>。
* <code>||</code>(論理OR):どちらか一方が<code>true</code>のときに<code>true</code>。
* <code>!</code>(論理NOT):真理値を反転させます。
例:
:<syntaxhighlight lang=rust copy>
let a = true;
let b = false;
println!("a && b: {}", a && b); // 出力: a && b: false
println!("!a: {}", !a); // 出力: !a: false
</syntaxhighlight>
=== ビット演算子 ===
ビット演算子は、整数型に対してビット単位の操作を行います。
* <code>&</code>(ビットAND):対応するビットが両方とも1の場合に1。
* <code>|</code>(ビットOR):対応するビットのどちらかが1の場合に1。
* <code>^</code>(ビットXOR):対応するビットが異なる場合に1。
* <code><<</code>(左シフト):ビットを指定した数だけ左に移動。
* <code>>></code>(右シフト):ビットを指定した数だけ右に移動。
例:
:<syntaxhighlight lang=rust copy>
let n = 0b1100; // 12 (2進数)
println!("n << 1: {:b}", n << 1); // 出力: 11000 (24)
println!("n & 0b1010: {:b}", n & 0b1010); // 出力: 1000 (8)
</syntaxhighlight>
=== その他の演算子 ===
* <code>=</code>(代入):変数に値を代入します。ただし、Rustではイミュータブルな変数に対して代入はできません。
* <code>+=</code>、<code>-=</code>、<code>*=</code>、<code>/=</code>、<code>%=</code>:加算や減算などを行った結果を代入します。
例:
:<syntaxhighlight lang=rust copy>
let mut z = 5;
z += 3; // zは8になります
</syntaxhighlight>
Rustの演算子は型ごとに適切な制約が設けられており、不正な操作をコンパイル時に防ぐ仕組みが備わっています。これにより、安全かつ効率的なコードの記述が可能となります。
=== 演算子オーバーロード ===
Rustでは、一部の演算子をオーバーロードすることが可能です。演算子オーバーロードは、<code>std::ops</code>モジュールに定義されているトレイトを実装することで実現します。これにより、独自の型に対して演算子の動作を定義することができます。
たとえば、加算演算子(<code>+</code>)をオーバーロードするには、<code>std::ops::Add</code>トレイトを実装します。このトレイトには<code>add</code>メソッドが含まれ、演算の具体的な挙動を指定します。
==== 加算演算子のオーバーロード例 ====
以下は、ベクトル型に対して加算演算子をオーバーロードする例です。
:<syntaxhighlight lang=rust copy>
use std::ops::Add;
#[derive(Debug)]
struct Vector {
x: i32,
y: i32,
}
impl Add for Vector {
type Output = Vector;
fn add(self, other: Vector) -> Vector {
Vector {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
fn main() {
let v1 = Vector { x: 1, y: 2 };
let v2 = Vector { x: 3, y: 4 };
let v3 = v1 + v2; // 加算演算子が使用可能
println!("{:?}", v3); // 出力: Vector { x: 4, y: 6 }
}
</syntaxhighlight>
この例では、<code>Add</code>トレイトの<code>add</code>メソッドを実装することで、<code>Vector</code>型に対して<code>+</code>演算子を適用できるようにしています。
==== 他の演算子のオーバーロード ====
Rustでは他にも多くの演算子がトレイトとして定義されており、同様にオーバーロードが可能です。以下はその一部です。
* <code>std::ops::Sub</code>:減算演算子(<code>-</code>)
* <code>std::ops::Mul</code>:乗算演算子(<code>*</code>)
* <code>std::ops::Div</code>:除算演算子(<code>/</code>)
* <code>std::ops::Rem</code>:剰余演算子(<code>%</code>)
* <code>std::ops::Neg</code>:単項マイナス(<code>-</code>)
* <code>std::ops::BitAnd</code>:ビットAND(<code>&</code>)
* <code>std::ops::Shl</code>:左シフト(<code><<</code>)
==== 注意点 ====
演算子オーバーロードは便利ですが、過剰な使用はコードの可読性を損なう可能性があります。特に、直感的でない動作を定義する場合は、利用者に混乱を与えるおそれがあります。そのため、オーバーロードを使用する際は、型の意味や使用意図を明確に反映させることが重要です。
このように、Rustの演算子オーバーロード機能は、型安全性と柔軟性を両立させながら、独自の型に対して直感的な操作を実現する手段を提供します。
== パターンマッチング ==
=== パターンマッチングの概要 ===
==== パターンマッチングの基本概念 ====
パターンマッチングは、プログラミング言語において特定のパターンとデータを照合し、条件に基づいた処理を行う手法です。この手法は、コードの可読性や柔軟性を高めるために広く使用されます。パターンマッチングの基本的な概念は、与えられたデータが特定のパターンに一致するかどうかを検査し、それに応じた処理を行います。
==== Rustにおけるパターンマッチングの役割と重要性 ====
Rustでは、パターンマッチングは非常に重要な機能です。パターンマッチングは、異なる条件に基づいてコードをブロックに分割し、それぞれの条件に対して適切な処理を行うことができます。これにより、コードの複雑さが軽減され、可読性が向上します。また、Rustの型システムとの統合により、安全性やエラーハンドリングの向上も実現されます。
=== 基本的なパターン ===
==== リテラルパターン ====
リテラルパターンは、値そのものとマッチさせるための最も基本的なパターンです。以下の例では、<code>x</code>の値が<code>1</code>の場合にマッチします。
:<syntaxhighlight lang=rust copy>
let x = 1;
match x {
1 => println!("one"),
_ => println!("not one"),
}
</syntaxhighlight>
==== 特定の値とのマッチング ====
リテラルパターンは、数値や文字列、真理値などの様々なリテラル値とマッチさせることができます。
:<syntaxhighlight lang=rust copy>
let x = 'c';
match x {
'a' => println!("apple"),
'b' => println!("banana"),
'c' => println!("cherry"), // この行が実行される
_ => println!("other"),
}
</syntaxhighlight>
==== 変数パターン ====
変数パターンは、値を新しい変数にバインドするためのパターンです。以下の例では、<code>x</code>の値が<code>5</code>の場合に<code>y</code>に<code>5</code>がバインドされます。
:<syntaxhighlight lang=rust copy>
let x = 5;
match x {
y => println!("x is {}", y), // この行が実行され、y = 5
}
</syntaxhighlight>
==== 値を変数にバインドするパターン ====
変数パターンは、値をパターン内の変数にバインドすることができます。これは、値を後で使用したり、条件分岐に利用したりするのに便利です。
:<syntaxhighlight lang=rust copy>
let x = 10;
match x {
0 => println!("x is zero"),
y if y > 0 => println!("x is positive: {}", y), // この行が実行される
y => println!("x is negative: {}", y),
}
</syntaxhighlight>
==== ワイルドカードパターン ====
ワイルドカードパターン(<code>_</code>)は、任意の値とマッチします。これは、特定の値を無視したい場合や、残りのパターンを捕捉したい場合に便利です。
:<syntaxhighlight lang=rust copy>
let x = 42;
match x {
0 => println!("x is zero"),
_ => println!("x is something else"), // この行が実行される
}
</syntaxhighlight>
==== 任意の値とのマッチング ====
ワイルドカードパターンを使えば、任意の値とマッチさせることができます。これは、値を確認する必要がない場合や、デフォルトの処理を実行したい場合に役立ちます。
:<syntaxhighlight lang=rust copy>
let x = 123;
match x {
0 => println!("x is zero"),
_ => println!("x is not zero"), // この行が実行される
}
</syntaxhighlight>
=== 列挙型とパターンマッチング ===
==== 列挙型の定義と使い方 ====
Rustには、列挙型と呼ばれる特別な型があります。列挙型は、いくつかの列挙値のいずれかを取ることができる型です。以下の例では、<code>Direction</code>という列挙型を定義しています。
:<syntaxhighlight lang=rust copy>
enum Direction {
Up,
Down,
Left,
Right,
}
</syntaxhighlight>
列挙型の値を作成するには、列挙型名とコロン(<code>:</code>)を使用します。
:<syntaxhighlight lang=rust copy>
let up = Direction::Up;
let down = Direction::Down;
</syntaxhighlight>
==== 列挙型に対するパターンマッチングの活用 ====
列挙型とパターンマッチングを組み合わせると、非常に強力なコードを書くことができます。以下の例では、<code>Direction</code>列挙型の値に対してパターンマッチングを行っています。
:<syntaxhighlight lang=rust copy>
enum Direction {
Up,
Down,
Left,
Right,
}
fn get_direction_name(dir: Direction) -> &'static str {
match dir {
Direction::Up => "上",
Direction::Down => "下",
Direction::Left => "左",
Direction::Right => "右",
}
}
fn main() {
let up = Direction::Up;
let down = Direction::Down;
println!("up: {}", get_direction_name(up)); // 上
println!("down: {}", get_direction_name(down)); // 下
}
</syntaxhighlight>
この例では、<code>get_direction_name</code>関数が列挙型<code>Direction</code>の値に対してパターンマッチングを行い、対応する文字列を返しています。
=== 構造体とタプルのパターンマッチング ===
==== 構造体の定義と使い方 ====
Rustでは、構造体を使ってデータを表現することができます。構造体は、フィールドと呼ばれる複数の値を持つことができます。以下の例では、<code>Person</code>という構造体を定義しています。
:<syntaxhighlight lang=rust copy>
struct Person {
name: String,
age: u32,
}
</syntaxhighlight>
構造体のインスタンスを作成するには、構造体名とフィールド値を指定します。
:<syntaxhighlight lang=rust copy>
let person = Person {
name: String::from("Alice"),
age: 30,
};
</syntaxhighlight>
==== タプルの定義と使い方 ====
タプルは、異なる型の値を含むことができる集合体です。タプルは括弧<code>()</code>で囲んで定義します。
:<syntaxhighlight lang=rust copy>
let tuple = (1, 3.14, "hello");
</syntaxhighlight>
タプルの要素にアクセスするには、インデックスを使用します。
:<syntaxhighlight lang=rust copy>
let x = tuple.0; // 1
let y = tuple.1; // 3.14
let z = tuple.2; // "hello"
</syntaxhighlight>
==== 構造体とタプルに対するパターンマッチングの活用 ====
構造体やタプルに対してパターンマッチングを行うことができます。これは、データの構造に基づいて処理を行う場合に非常に便利です。
:<syntaxhighlight lang=rust copy>
struct Person {
name: String,
age: u32,
}
fn print_person_info(person: Person) {
match person {
Person { name, age } => println!("名前: {}, 年齢: {}", name, age),
}
}
fn main() {
let alice = Person {
name: String::from("Alice"),
age: 30,
};
print_person_info(alice);
}
</syntaxhighlight>
この例では、<code>print_person_info</code>関数が<code>Person</code>構造体のインスタンスに対してパターンマッチングを行い、名前と年齢を出力しています。
タプルに対してもパターンマッチングを行うことができます。
:<syntaxhighlight lang=rust copy>
fn print_tuple_info(tuple: (u32, f64, &str)) {
match tuple {
(x, y, z) => println!("x: {}, y: {}, z: {}", x, y, z),
}
}
fn main() {
let tuple = (42, 3.14, "hello");
print_tuple_info(tuple);
}
</syntaxhighlight>
この例では、<code>print_tuple_info</code>関数がタプル<code>(u32, f64, &str)</code>に対してパターンマッチングを行い、その要素を出力しています。
=== パターンガード ===
==== パターンガードの概要 ====
パターンガードは、パターンマッチングに条件を追加するための機能です。パターンに一致する値に対して、追加の条件を指定することで、より柔軟な処理を行うことができます。
==== パターンガードの実装方法と使いどころ ====
パターンガードは、パターンに矢印(<nowiki><code>=></code></nowiki>)と条件式を追加することで実装します。条件式が真の場合のみ、パターンに一致したものとして処理されます。
:<syntaxhighlight lang=rust copy>
let x = 10;
match x {
2 | 5 | 10 if x % 2 == 0 => println!("x は偶数です"),
_ => println!("x は奇数です"),
}
</syntaxhighlight>
この例では、<code>x</code> が 2、5、10 のいずれかで、かつ偶数の場合は、"x は偶数です" と出力されます。
パターンガードは、様々な場面で活用することができます。
* 特定の条件を満たす値のみを処理したい場合
* エラー処理を行う場合
* 複雑なパターンを処理する場合
==== ネストしたパターン ====
==== ネストしたパターンの例 ====
ネストしたパターンは、複数のパターンを組み合わせたパターンです。パターンガードと組み合わせることで、より複雑な条件を処理することができます。
:<syntaxhighlight lang=rust copy>
enum Color {
Red,
Green,
Blue,
}
struct Point {
x: i32,
y: i32,
color: Color,
}
let point = Point { x: 10, y: 20, color: Color::Red };
match point {
Point { x, y, color: Color::Red } => println!("赤い点が ({}, {}) にあります", x, y),
Point { x, y, color } => println!("({}, {}) に {} 色の点がいます", x, y, color),
}
</syntaxhighlight>
この例では、<code>point</code> が <code>Point { x, y, color: Color::Red }</code> の形式の構造体である場合のみ、"赤い点が ({}, {}) にあります" と出力されます。<code>point</code> が <code>Point { x, y, color }</code> の形式の構造体である場合は、"({}, {}) に {} 色の点がいます" と出力され、<code>color</code> には <code>point</code> の <code>color</code> フィールドの値が格納されます。
==== ネストしたパターンの利点と注意点 ====
ネストしたパターンを使用すると、複雑な条件を処理しやすくなります。しかし、パターンが複雑になりすぎると、コードが読みづらくなる可能性があります。
==== エラーハンドリングとパターンマッチング ====
==== ResultやOptionとの組み合わせ ====
<code>Result</code> や <code>Option</code> などの型とパターンマッチングを組み合わせることで、エラーハンドリングを効率的に行うことができます。
:<syntaxhighlight lang=rust copy>
let result = read_file("filename.txt");
match result {
Ok(contents) => println!("ファイルの内容: {}", contents),
Err(error) => println!("エラー: {}", error),
}
</syntaxhighlight>
この例では、<code>read_file</code> 関数が成功した場合のみ、ファイルの内容が出力されます。失敗した場合は、エラーメッセージが出力されます。
==== エラーハンドリングにおけるパターンマッチングの有用性 ====
パターンマッチングを使用すると、エラー処理をより簡潔かつ分かりやすく記述することができます。また、エラーの種類ごとに異なる処理を行うこともできます。
=== 高度なパターンマッチング ===
==== パターンマッチングにおける複雑なパターンの扱い方 ====
パターンマッチングは、複雑なパターンを処理するために様々な機能を提供しています。
; パターンガード : パターンに一致する値に対して、追加の条件を指定することができます。
; ネストしたパターン : 複数のパターンを組み合わせたパターンを作成することができます。
; パターン分解 : パターンに一致した値を複数の変数に格納することができます。
; ガード付きパターン : パターンに一致する値に対して、条件分岐を実行することができます。
これらの機能を組み合わせることで、複雑なデータ構造を効率的に処理することができます。
==== 複数のパターンに一致する場合の処理方法 ====
複数のパターンに一致する場合は、パターンガードやネストしたパターンを使用して、どのパターンに一致するかを判別する必要があります。
== マクロ ==
Rustのマクロは、<code>macro_rules!</code>を使ったマクロと<code>proc_macro</code>を使ったプロシージャマクロの2種類があります。<code>macro_rules!</code>を使ったマクロはパターンマッチングを用いて簡易的なマクロを定義します。一方、<code>proc_macro</code>を使ったプロシージャマクロは、Rustのコードを受け取り、変換したり、新しいコードを生成するためのより柔軟なマクロです。
=== <code>macro_rules!</code>を使ったマクロ ===
<code>macro_rules!</code>は、パターンに基づいてマッチングし、そのパターンに一致した場合に指定されたコードを生成する簡易なマクロを定義します。
例えば、<code>vec!</code>マクロは、可変長のベクタを生成するマクロです。これは<code>macro_rules!</code>を使って次のように実装されています。
:<syntaxhighlight lang=rust line copy>
macro_rules! vec {
// パターンマッチで要素を取得して新しいベクタを生成
( $( $x:expr ),* ) => {
{
let mut temp_vec = Vec::new();
$(temp_vec.push($x);)*
temp_vec
}
};
}
fn main() {
// `vec!`マクロを使ってベクタを生成
let my_vec = vec![1, 2, 3, 4];
// 生成されたベクタを表示
println!("{:?}", my_vec);
}
</syntaxhighlight>
これは、<code>vec![1, 2, 3]</code>を使うと、<code>[1, 2, 3]</code>というベクタを生成します。このマクロは、<code>$( $x:expr ),*</code>のパターンに一致して、指定された式(<code>$x</code>)をベクタに挿入するコードを生成します。
=== <code>proc_macro</code>を使ったプロシージャマクロ ===
<code>macro_rules!</code>を使わずにマクロを定義する方法もあります。これは、プロシージャマクロ(<code>proc_macro</code>)を使用した方法です。<code>proc_macro</code>は、<code>macro</code>キーワードによって定義される関数の一種で、Rustのコードを受け取り、そのコードを操作して変換することができます。
例えば、<code>vec!</code>マクロを<code>macro_rules!</code>ではなく、プロシージャマクロとして定義する場合は、<code>proc_macro</code>を使います。
:<syntaxhighlight lang=rust line copy>
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Expr, parse_quote};
#[proc_macro]
pub fn my_vec(input: TokenStream) -> TokenStream {
// 入力をパース
let input_expr: Expr = parse_macro_input!(input);
// 入力を取得して新しいベクタを生成するコードを生成
let expanded = quote! {
{
let mut temp_vec = Vec::new();
temp_vec.push(#input_expr);
temp_vec
}
};
// TokenStreamに変換して返す
TokenStream::from(expanded)
}
</syntaxhighlight>
この例では、<code>proc_macro</code>として<code>my_vec</code>という新しいマクロを定義しています。<code>my_vec</code>は、<code>proc_macro</code>の関数として定義され、Rustのコードを受け取り、それを操作して新しいコードを生成します。
このプロシージャマクロを使うと、次のようにマクロを呼び出すことができます。
:<syntaxhighlight lang=rust line copy>
fn main() {
let my_vec = my_vec!(42);
println!("{:?}", my_vec); // [42]
}
</syntaxhighlight>
この例では、<code>my_vec!</code>マクロを使用して、引数として<code>42</code>を渡しています。このマクロは、引数を含むベクタを生成するもので、<code>my_vec!(42)</code>は<code>[42]</code>というベクタを生成します。
この方法では、<code>proc_macro</code>を使用して、<code>macro_rules!</code>を使わずに独自のマクロを定義できます。ただし、この方法では<code>proc_macro</code>と関連するライブラリ(<code>syn</code>、<code>quote</code>など)を使用する必要があります。
=== マクロ関数 ===
マクロ関数は、<code>macro_rules!</code>マクロを使って定義されます。
これは、マクロのパターンとそれに対する置換を定義するマクロです。
マクロの呼び出し元は、パターンにマッチする式を渡し、置換が適用されたコードが生成されます。
;マクロ関数の例:<syntaxhighlight lang=rust line copy>
macro_rules! say_hello {
() => {
println!("Hello, world!");
};
}
fn main() {
say_hello!();
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
Hello, world!
</syntaxhighlight>
;解説
:上記の例では、<code>say_hello!()</code>マクロを定義しています。これは、空の引数リストに対して<code>println!("Hello, world!")</code>を生成するマクロです。
=== マクロ属性 ===
マクロ属性はRustのコンパイル時にコードを修飾するために使用されます。
通常は、関数や構造体、列挙型、フィールドなどに適用されます。
マクロ属性を使用することで、コンパイル時に生成されるコードに追加の情報を提供できます。
;マクロ属性の例:<syntaxhighlight lang=rust line copy>
#[derive(Debug)]
struct MyStruct {
my_field: i32,
}
fn main() {
let my_struct = MyStruct { my_field: 42 };
println!("{:?}", my_struct);
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
MyStruct { my_field: 42 }
</syntaxhighlight>
;解説
:上記の例では、<code>#[derive(Debug)]</code>マクロ属性を使用しています。これは、<code>MyStruct</code>に<code>Debug</code>トレイトを実装するために必要なコードを生成するマクロ属性です。<code>println!</code>マクロで<code>my_struct</code>を表示する際に、<code>Debug</code>トレイトのメソッドを呼び出して情報を表示することができます。
== クレートとは ==
* '''Rustのパッケージ単位:''' Rustでは、コードのパッケージングや再利用のために「クレート」(Crate)という単位が使われます。1つのクレートには、1つ以上の関連するモジュールやデータ型が含まれます。
* '''Cargoによる管理:''' クレートは、RustのパッケージマネージャであるCargoによって管理されます。Cargoは、クレートの作成、ビルド、依存関係の解決などを自動化するツールです。
{{See also|Cargoハンドブック}}
=== クレートの構造 ===
典型的なRustのクレートは、次のような構造を持ちます:
:<syntaxhighlight lang=text>
my_crate/
├── src/
│ ├── main.rs
│ ├── lib.rs
│ └── other_module.rs
├── Cargo.toml
└── README.md
</syntaxhighlight>
* '''src/:''' クレートのソースコードを含むディレクトリ。
* '''src/main.rs:''' クレートを実行するためのエントリポイント。
* '''src/lib.rs:''' ライブラリとしてコンパイルされる場合に使用されるエントリポイント。
* '''src/other_module.rs:''' 他のモジュール。
* '''Cargo.toml:''' クレートのメタデータ、依存関係、ビルド構成などを含むファイル。
* '''README.md:''' クレートのドキュメント。
=== クレートの作成 ===
新しいクレートを作成する手順は以下の通りです:
# <code>cargo new</code>コマンドを使用して新しいクレートのディレクトリを作成します。
# <code>Cargo.toml</code>ファイルを編集して、クレートのメタデータを定義します。
# 必要に応じて、<code>src/main.rs</code>や他のモジュールを編集して、クレートのコードを実装します。
# <code>cargo run</code>コマンドを使用してクレートをビルドして実行します。
=== クレートの公開 ===
クレートを他の開発者と共有するためには、それをcrates.ioという公式のクレートレジストリに公開する必要があります。公開手順は以下の通りです:
# crates.ioにアカウントを作成します。
# <code>cargo login</code>コマンドを使用してcrates.ioにログインします。
# クレートのバージョン番号を更新します。
# <code>cargo publish</code>コマンドを使用してクレートを公開します。
=== まとめ ===
Rustのクレートは、コードのパッケージングや再利用を容易にする重要な概念です。Cargoを使用してクレートを管理し、必要に応じてcrates.ioに公開することで、他の開発者とクレートを共有することができます。
Rustにおいて、パッケージはクレートと呼ばれます。クレートは、Rustのコードやライブラリをパッケージ化して共有できるようにするための仕組みです。クレートは、依存関係を解決するためのメタデータと、コードとその他のファイルから構成されています。
Rustには、<code>crates.io</code>という公式のクレートレジストリがあり、開発者はそこでクレートを共有したり、他の開発者が作成したクレートを使用したりできます。
<!--
*クレートとは何か
**クレートの概要
**クレートとは何を提供するか
**クレートの種類(バイナリ、ライブラリ、依存関係)
*クレートの作成
**クレートの作成方法
**クレートの名前とバージョン管理
**クレートのファイル構造
**クレートのライセンス
*クレートのビルド
**Cargoの概要
**Cargoのインストールと初期化
**Cargo.tomlファイルの説明
*クレートのビルド方法
*クレートのテスト
**テストの重要性
**ユニットテストと統合テストの違い
**テストの書き方と実行方法
**テストの結果の確認方法
*クレートの公開
**クレートの公開方法
**crates.ioにクレートをアップロードする方法
**クレートのバージョン管理と更新方法
**クレートのドキュメントの生成と公開方法
*クレートの依存関係
**依存関係の概要
**依存関係の設定方法
**Cargo.lockファイルの役割
**依存関係のアップデート方法
*クレートのプロジェクト管理
**複数のクレートを管理する方法
**クレートの相互依存関係
**ワークスペースの作成と利用方法
**ワークスペースでの依存関係の管理方法
-->
== 代数的データ型 ==
Rustにおける代数的データ型は、構造体(<code>struct</code>)と列挙型(<code>enum</code>)を指します。これらは複雑なデータ構造を表現するのに役立ちます。
=== 構造体(struct) ===
構造体は異なる型のフィールドを持つことができ、それぞれのフィールドは名前を持ちます。例えば:
:<syntaxhighlight lang=rust copy>
struct Point {
x: i32,
y: i32,
}
impl Point {
fn new(x: i32, y: i32) -> Self {
Self { x, y }
}
fn print(&self) {
println!("x: {}, y: {}", self.x, self.y);
}
}
fn main() {
let origin = Point::new(0, 0);
origin.print();
}
</syntaxhighlight>
=== 列挙型(enum) ===
列挙型は、いくつかの異なるバリアント(variant)の中から選択することができます。それぞれのバリアントはデータを持つことができます。
:<syntaxhighlight lang=rust copy>
enum Shape {
Circle(f64), // 半径を持つ
Rectangle(f64, f64), // 幅と高さを持つ
Square(f64), // 1辺の長さを持つ
}
impl Shape {
fn area(&self) -> f64 {
match self {
Shape::Circle(radius) => std::f64::consts::PI * radius * radius,
Shape::Rectangle(width, height) => width * height,
Shape::Square(side) => side * side,
}
}
}
fn main() {
let shapes = vec![
Shape::Circle(5.0),
Shape::Rectangle(10.0, 20.0),
Shape::Square(15.0),
];
for shape in &shapes {
println!("Area: {}", shape.area());
}
}
</syntaxhighlight>
これらの代数的データ型は、Rustで柔軟なデータ構造を表現する際に役立ちます。
== 属性(Attribute) ==
Rustの属性(Attribute)は、コンパイラに対してコードに関する追加情報や指示を提供するための注釈です。これらはコードの振る舞いや最適化、データレイアウト、コード生成に影響を与えます。属性は<code>#[...]</code>の形式で記述され、コンパイラやコードの挙動を制御します。Rustでは、属性を使用することでコードの効率性や可読性を高め、特定の機能を拡張できます。
==== よく使用される属性 ====
# '''<code>#[derive(...)]</code>'''
#* 自動導出(Derive Attribute)は、特定のトレイトを自動的に実装するために使用されます。
#* 例: <code>#[derive(Debug)]</code>は、構造体や列挙型に<code>Debug</code>トレイトを自動的に実装させ、<code>println!</code>でその内容を表示可能にします。
# '''<code>#[cfg(...)]</code>'''
#* コンパイル時の条件を指定するために使います。条件に応じてコードの一部をコンパイルするかどうかを制御します。
#* 例: <code>#[cfg(target_os = "linux")]</code>は、ターゲットOSがLinuxの場合のみ有効です。
# '''<code>#[allow(...)]</code> / <code>#[deny(...)]</code> / <code>#[warn(...)]</code>'''
#* コンパイラ警告のレベルを制御します。これにより、警告を許可、エラー化、または警告として表示させることができます。
#* 例: <code>#[deny(warnings)]</code>は、すべての警告をエラーとして扱います。
# '''<code>#[repr(...)]</code>'''
#* データのレイアウト方法を指定します。構造体や列挙型のメモリ配置を変更するために使用されます。
#* 例: <code>#[repr(C)]</code>は、C言語と互換性のあるレイアウトを指定します。
# '''<code>#[inline]</code> / <code>#[noinline]</code>'''
#* インライン展開の制御を行います。関数呼び出しのオーバーヘッドを削減するために使われ、最適化のためにインライン展開を促進します。
#* 例: <code>#[inline(always)]</code>は、関数が常にインライン展開されるように指定します。
# '''<code>#[test]</code> / <code>#[bench]</code>'''
#* ユニットテストやベンチマークテストをマークするために使います。
#* 例: <code>#[test]</code>は関数をテスト対象として指定し、テストランナーによって自動的に実行されます。
# '''<code>#[macro_use]</code>'''
#* マクロを利用する際に、外部クレートからマクロをインポートするために使います。
#* 例: <code>#[macro_use] extern crate serde_json;</code>で、<code>serde_json</code>クレートのマクロを利用できます。
# '''<code>#[feature(...)]</code>'''
#* 実験的な機能を有効にするために使われます。安定版Rustで使用するには、<code>nightly</code>ビルドが必要です。
#* 例: <code>#[feature(proc_macro)]</code>はプロシージャマクロ機能を有効にします。
=== Debugトレイト ===
<code>Debug</code>トレイトは、構造体や列挙型などのデータ構造をフォーマットして表示するために使われます。このトレイトを実装することで、デバッグ情報を簡単に出力することができます。
==== 使用例 ====
:<syntaxhighlight lang=rust>
#[derive(Debug)]
struct MyStruct {
name: String,
age: u32,
}
fn main() {
let my_data = MyStruct {
name: String::from("Alice"),
age: 30,
};
// Debugトレイトを使用して構造体を表示
println!("My data: {:?}", my_data);
}
</syntaxhighlight>
;出力例:
:<syntaxhighlight lang=text>
My data: MyStruct { name: "Alice", age: 30 }
</syntaxhighlight>
このコード例では、<code>#[derive(Debug)]</code>を使って<code>MyStruct</code>構造体に<code>Debug</code>トレイトを実装し、<code>println!</code>マクロで<code>{:?}</code>フォーマット指定子を使って内容を表示しています。<code>Debug</code>トレイトを手動で実装することなく、簡単にデバッグ情報を出力できます。
=== その他の属性 ===
* '''<code>#[allow(dead_code)]</code>'''
** 使用されていないコードに対する警告を無視します。特にデバッグコードや一時的な実装で便利です。
* '''<code>#[cfg_attr(...)]</code>'''
** 条件付きで属性を適用するために使います。<code>#[cfg]</code>と組み合わせることで、特定の条件下で属性を追加できます。
* '''<code>#[must_use]</code>'''
** 戻り値が使われなければコンパイラから警告を出します。戻り値を意図的に無視することを防ぎます。
Rustの属性は、コードの最適化やコンパイラの挙動、エラーチェックを制御するために強力なツールです。それぞれの属性は特定の目的を持っており、正しく使用することでコードの品質を向上させることができます。
== ジェネリックス ==
Rustにおけるジェネリクスは、特定の型に依存せず、複数の型で動作するコードを作成するための重要な機能です。ジェネリクスを使用することで、同じコードを複数の型で再利用したり、型安全性を保ちながら柔軟性を持たせたりすることができます。
=== 基本的なジェネリクスの使用 ===
:<syntaxhighlight lang=rust copy>
// Tというジェネリックな型を持つ関数
fn print_value<T: std::fmt::Display>(value: T) {
println!("Value is: {}", value);
}
fn main() {
// 使用例
print_value(10); // Tはi32として推論される
print_value(2.73 as f32); // Tはf32として推論される
print_value("Hello"); // Tは&strとして推論される
}
</syntaxhighlight>
:この例では、<code>print_value</code>関数がジェネリックな型<code>T</code>を持ち、<code>T</code>は<code>std::fmt::Display</code>トレイトを実装している型に制限されています。<code>std::fmt::Display</code>トレイトは、<code>{}</code>でフォーマット可能な型を表します。
:引数<code>value</code>の型はコンパイル時に推論されます。
=== ジェネリックな構造体 ===
:<syntaxhighlight lang=rust copy>
// ジェネリックな構造体
struct Pair<T, U> {
first: T,
second: U,
}
fn main() {
// 使用例
let pair_of_int_and_str = Pair { first: 10, second: "Hello" };
println!("first = {:?}, second = {:?}", pair_of_int_and_str.first, pair_of_int_and_str.second);
}
</syntaxhighlight>
:<code>Pair</code>構造体は2つの異なる型を持つことができます。使用する際に具体的な型を指定することで、ジェネリックな構造体を作成できます。
=== ジェネリックなトレイト ===
:<syntaxhighlight lang=rust copy>
// ジェネリックなトレイト
trait Printable {
fn print(&self);
}
// TがPrintableトレイトを実装していることを要求する関数
fn print_trait<T: Printable>(item: T) {
item.print();
}
// 使用例
struct MyType;
impl Printable for MyType {
fn print(&self) {
println!("Printing MyType");
}
}
fn main() {
let obj = MyType;
print_trait(obj); // Printableトレイトを実装したMyTypeのインスタンスを受け取る
}
</syntaxhighlight>
:ここでは、<code>Printable</code>というジェネリックなトレイトを定義し、<code>print_trait</code>関数で<code>Printable</code>トレイトを実装した型<code>T</code>を受け取る方法を示しています。
ジェネリクスは、関数、構造体、列挙型、トレイトなどのRustのさまざまな要素で使用できます。これにより、柔軟性のあるコードを作成し、再利用性を高めることができます。
=== 型制約 ===
ジェネリックスにおいては、型制約(type constraint)は、ジェネリックな型パラメータに対して特定の条件やトレイトの制約を課すことを指します。これにより、ジェネリックな型が特定の性質を持つことを保証し、安全性を確保することができます。
Rustでは、<code>trait</code>を使用して型制約を実装します。例えば、<code>std::fmt::Display</code>トレイトを持つ型に制約を課したい場合、以下のように実装します。
:<syntaxhighlight lang=rust copy>
fn print_value<T: std::fmt::Display>(value: T) {
println!("Value is: {}", value);
}
</syntaxhighlight>
ここでは<code><T: std::fmt::Display></code>という構文を使用して、<code>T</code>が<code>std::fmt::Display</code>トレイトを実装している必要があることを宣言しています。この制約により、<code>print_value</code>関数は<code>std::fmt::Display</code>トレイトを実装した型に対してのみ呼び出しが可能になります。
また、複数のトレイト制約を持つこともできます。例えば、<code>std::fmt::Debug</code>と<code>std::fmt::Display</code>トレイトの両方を実装した型を要求する場合は、次のように書きます。
:<syntaxhighlight lang=rust copy>
fn print_value<T: std::fmt::Debug + std::fmt::Display>(value: T) {
println!("Value is: {:?}", value);
}
#[derive(Debug)]
struct Point<T> {
x: T,
y: T,
}
// Point<T>型に対するDisplayトレイトの実装
impl<T: std::fmt::Display> std::fmt::Display for Point<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Point({}, {})", self.x, self.y)
}
}
fn main() {
let int_point = Point { x: 5, y: 10 };
print_value(int_point); // `Point`型は`std::fmt::Debug`と`std::fmt::Display`を実装している
let float_point = Point { x: 1.5, y: 3.2 };
print_value(float_point); // 同様に、`Point`型は`std::fmt::Debug`と`std::fmt::Display`を実装している
}
</syntaxhighlight>
<code>T</code>が<code>std::fmt::Debug</code>と<code>std::fmt::Display</code>の両方を実装している必要があります。
これにより、ジェネリックなコードをより安全に、かつ特定の条件下で使用できるように制約を課すことができます。
== エラーハンドリング ==
Rustでは、<code>Result</code>型と<code>panic</code>によるエラーハンドリングが一般的です。<code>Result</code>型は成功または失敗を表す列挙型で、<code>Ok</code>は成功時の値、<code>Err</code>はエラー時の情報を持ちます。
まず、<code>Result</code>型を使用した例を見てみましょう:
:<syntaxhighlight lang=rust copy>
use std::fs::File;
use std::io::{self, Read};
fn read_file_contents(file_path: &str) -> Result<String, io::Error> {
let mut file = File::open(file_path)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
Ok(contents)
}
fn main() {
let file_path = "/etc/hosts";
match read_file_contents(file_path) {
Ok(contents) => println!("File contents: {}", contents),
Err(e) => println!("Error reading file: {:?}", e),
}
}
</syntaxhighlight>
この例では、<code>read_file_contents</code>関数が<code>Result<String, io::Error></code>を返します。これは、成功時には文字列を<code>Ok</code>で、エラー時には<code>io::Error</code>を<code>Err</code>で返すことを示しています。ファイルを開いたり、読み込んだりするメソッドの呼び出しには<code>?</code>演算子を使用し、エラーが発生した場合は早期リターンしてエラーを返します。
<code>match</code>ブロックでは、<code>read_file_contents</code>の戻り値に対して<code>Ok</code>と<code>Err</code>の両方の可能性に対処し、それぞれ成功時の振る舞いとエラー時の振る舞いを定義しています。これにより、関数の呼び出し元で適切にエラーハンドリングを行うことができます。
=== except() ===
また、Rustには<code>panic!</code>マクロを使用してプログラムを異常終了させる方法もあります。これは致命的なエラーが発生した場合に使用されますが、エラーを適切に処理するために<code>Result</code>型を使うことが推奨されます。
:<syntaxhighlight lang=rust copy>
fn main() {
let v = vec![1, 2, 3];
let index = 5;
let value = v.get(index).expect("Failed to get the value at index");
println!("Value at index {}: {}", index, value);
}
</syntaxhighlight>
この例では、<code>get</code>メソッドは<code>Option</code>型を返し、<code>expect</code>メソッドは<code>Some</code>の場合は中の値を返し、<code>None</code>の場合は指定したメッセージとともにプログラムを<code>panic</code>させます。
ただし、<code>expect</code>を使う際には、<code>panic</code>が発生した際に表示されるメッセージは注意深く選ぶ必要があります。
=== panic!() ===
<code>panic!()</code>は通常、予期せぬ状況やプログラムの継続が不可能な状況で使われることがあります。以下はそのような例です。
:<syntaxhighlight lang=rust copy>
fn divide(a: i32, b: i32) -> i32 {
if b == 0 {
panic!("Attempted to divide by zero!"); // 0で割ろうとした場合、致命的なエラーとしてpanic!()を呼ぶ
}
a / b // 正常な割り算を行う
}
fn main() {
let dividend = 10;
let divisor = 0;
let result = divide(dividend, divisor); // divide関数を呼び出す
println!("Result of division: {}", result);
}
</syntaxhighlight>
この例では、<code>divide()</code>関数で<code>b</code>が0の場合に<code>panic!()</code>が呼ばれています。0で割ることは数学的に定義されていないため、これは致命的なエラーであり、プログラムの実行を中断させます。
<code>panic!()</code>は、このような状況に直面した場合に、プログラムを停止させるための手段として使われます。しかし、このようなエラーは通常、<code>if</code>文や<code>match</code>文などの条件分岐を使用して事前にチェックして、エラーハンドリングを行うことが推奨されます。
<code>Result</code>を使ってエラーハンドリングを行うことで、<code>panic!()</code>に頼らずにエラーを適切に処理できます。
:<syntaxhighlight lang=rust copy>
fn divide(a: i32, b: i32) -> Result<i32, &'static str> {
if b == 0 {
Err("Attempted to divide by zero!") // 0で割ろうとした場合、エラーをResultで返す
} else {
Ok(a / b) // 正常な割り算の結果をOkで返す
}
}
fn main() {
let dividend = 10;
let divisor = 0;
match divide(dividend, divisor) {
Ok(result) => println!("Result of division: {}", result),
Err(e) => println!("Error: {}", e), // エラーを適切に処理する
}
}
</syntaxhighlight>
<code>divide()</code>関数は<code>Result<i32, &'static str></code>を返し、0で割るエラーの場合には<code>Err</code>を、正常な計算の場合には<code>Ok</code>を返します。<code>match</code>ブロックでは、<code>Ok</code>と<code>Err</code>の両方のケースを処理し、エラーが発生した場合にはエラーメッセージを表示します。
このように<code>Result</code>型を使用することで、エラーハンドリングを柔軟かつ安全に行うことができます。<code>panic!()</code>に頼るよりも、エラーを予測し、適切に処理する方が望ましいです。
=== Option型 ===
<code>Option</code>型は、何らかの値が存在するかどうかを表現するRustの列挙型です。<code>Option</code>型は<code>Some</code>と<code>None</code>の2つのバリアントを持ち、<code>Some</code>は値が存在することを示し、<code>None</code>は値が存在しないことを示します。
<code>Option</code>型は、特定の操作が値を返さない可能性がある場合や、値が存在しない場合にエラーを返す代わりに<code>None</code>を返すために使われます。このような状況では、<code>Result</code>型を使用せずに、<code>Option</code>型が利用されることがあります。
以下は<code>Option</code>型の例です:
:<syntaxhighlight lang=rust copy>
fn divide(a: i32, b: i32) -> Option<i32> {
if b == 0 {
None // 0で割ろうとした場合、Noneを返す
} else {
Some(a / b) // 正常な割り算の結果をSomeで返す
}
}
fn main() {
let dividend = 10;
let divisor = 0;
match divide(dividend, divisor) {
Some(result) => println!("Result of division: {}", result), // Someの場合は値を表示
None => println!("Error: Division by zero!"), // Noneの場合はエラーメッセージを表示
}
}
</syntaxhighlight>
この例では、<code>divide()</code>関数は<code>Option<i32></code>を返し、0で割るエラーの場合には<code>None</code>を、正常な計算の場合には<code>Some</code>を返します。<code>match</code>ブロックでは、<code>Some</code>と<code>None</code>の両方のケースを処理し、<code>None</code>の場合はエラーメッセージを表示します。
<code>Option</code>型は、特に値が存在しないことが普通に起こり得る場面で、エラー処理や結果の取り扱いを行う際に有用です。例外的な状況ではなく、むしろ普通の操作の一部として考えられる「値の有無」を扱う際に利用されることが多いです。
== イテレーター ==
Rustの<code>Iterator</code>トレイトは、コレクションやデータのシーケンスを反復処理するための非常に強力な機能です。<code>Iterator</code>は、<code>next()</code> メソッドを提供し、それを使用して次の要素を返し、シーケンスの終わりに達した場合は <code>None</code> を返します。
基本的な使い方は次のようになります:
# イテレータの作成: コレクションやデータからイテレータを作成します。<code>iter()</code>や<code>into_iter()</code>、<code>iter_mut()</code>などのメソッドを使用して、それぞれイミュータブルな参照、所有権、ミュータブルな参照を使ったイテレータを取得できます。
#:<syntaxhighlight lang=rust copy>
let numbers = vec![1, 2, 3, 4, 5];
let mut iter = numbers.iter(); // イミュータブルなイテレータ
</syntaxhighlight>
# <code>next()</code>を使用した反復処理: <code>next()</code>メソッドを使って、イテレータから次の要素を取得します。
#:<syntaxhighlight lang=rust copy>
match iter.next() {
Some(number) => println!("Number: {}", number),
None => println!("End of sequence"),
}
</syntaxhighlight>
# forループを使用した反復処理: 一般的には、<code>for</code>ループを使用してイテレータを処理します。
#:<syntaxhighlight lang=rust copy>
for number in numbers.iter() {
println!("Number: {}", number);
}
</syntaxhighlight>
=== Iteratorトレイト ===
<code>Iterator</code>トレイトは、<code>map()</code>、<code>filter()</code>、<code>fold()</code>などの便利なメソッドも提供しており、これらを組み合わせることでデータを効果的に処理できます。
:<syntaxhighlight lang=rust copy>
let numbers = vec![1, 2, 3, 4, 5];
// 各要素を2倍して新しいベクターを作成する
let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect();
// 偶数のみをフィルタリングする
let evens: Vec<i32> = numbers.into_iter().filter(|&x| x % 2 == 0).collect();
// 全要素を合計する
let sum: i32 = numbers.iter().sum();
</syntaxhighlight>
<code>Iterator</code>トレイトは、Rustでのデータ処理を非常に柔軟で効率的にします。関数型プログラミングの考え方に基づいた強力な機能を提供しています。
=== 独自イテレータの実装 ===
<code>Iterator</code>トレイトを使用して独自のイテレータを実装する例を示します。
例えば、0から始まり、指定されたステップごとに増加するカウンターを作成するイテレータを実装してみましょう。
:<syntaxhighlight lang=rust copy>
struct Counter {
current: u32,
step: u32,
max: Option<u32>,
}
impl Counter {
fn new(start: u32, step: u32, max: Option<u32>) -> Counter {
Counter {
current: start,
step,
max,
}
}
}
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
match self.max {
Some(max) if self.current >= max => None,
_ => {
let result = self.current;
self.current += self.step;
Some(result)
}
}
}
}
fn main() {
let counter = Counter::new(0, 2, Some(10));
for num in counter {
println!("{}", num);
}
}
</syntaxhighlight>
この例では、<code>Counter</code>構造体は<code>Iterator</code>トレイトを実装しています。<code>next()</code>メソッドは、現在の値を返し、カウンターを指定されたステップで増分させます。また、<code>max</code>値が設定されており、その値に達するとイテレータは終了します。
<code>main()</code>関数内では、<code>Counter</code>イテレータを使用して0から10まで2ずつ増加する数列を生成しています。
<code>Iterator</code>トレイトを実装することで、独自のイテレータを作成し、柔軟な反復処理を行うことができます。
== impl ==
<code>impl</code>キーワードは、Rustで特定の型に対してメソッドやトレイトを実装するために使用されます。基礎から応用的な使い方まで見ていきましょう。
=== 基礎的な使用例 ===
; メソッドの実装
:<syntaxhighlight lang=rust copy>
struct MyStruct {
value: i32,
}
impl MyStruct {
// MyStruct型に対するメソッドの実装
fn new(value: i32) -> MyStruct {
MyStruct { value }
}
fn get_value(&self) -> i32 {
self.value
}
}
fn main() {
let instance = MyStruct::new(42);
println!("Value: {}", instance.get_value()); // メソッド呼び出し
}
</syntaxhighlight>
<code>impl</code>ブロック内で、<code>MyStruct</code>に対する<code>new</code>メソッドと<code>get_value</code>メソッドを実装しています。<code>main</code>関数でこれらのメソッドを使用しています。
=== 応用的な使用例 ===
; トレイトの実装
:<syntaxhighlight lang=rust copy>
trait Printable {
fn print(&self);
}
struct MyType {
value: i32,
}
impl Printable for MyType {
fn print(&self) {
println!("Value: {}", self.value);
}
}
fn main() {
let obj = MyType { value: 10 };
obj.print();
}
</syntaxhighlight>
{{コラム|width=100%|トレイトとインターフェイスやプロトコルとの類似性|2=トレイトは他の言語でのインターフェースやプロトコルに似ています。これらの概念は、異なる型や構造体が共通の振る舞いを持つことを許可し、それによってポリモーフィズム(多様性)を実現します。
;インターフェース
:共通の振る舞い: インターフェースは、異なるクラスが共通の振る舞いを持つことを保証します。他の言語では、この振る舞いはインターフェースで定義され、クラスはそれを実装します。
:メソッドの宣言: インターフェースでは、クラスが実装しなければならないメソッドの宣言が含まれます。
:多重継承の代替: インターフェースは多重継承の代替手段としても使われ、クラスは複数のインターフェースを実装することができます。
;プロトコル
:抽象的な振る舞いの定義: プロトコルは、特定の振る舞いや機能を表す抽象的な規約です。Swiftなどの言語ではプロトコルが使われ、クラスや構造体はそれらを適合させます。
:メソッドの要求: プロトコルは、適合する型によって実装されるメソッドやプロパティの要求を定義します。
:型の適合性の強化: プロトコルに適合することで、様々な型を同じ抽象的な概念に束縛することができます。
;Rustのトレイト
:共通の振る舞いの提供: トレイトは、構造体や他の型に共通の振る舞いを提供します。それぞれの型はトレイトを実装することで、その振る舞いを持つことができます。
:メソッドの宣言: トレイトではメソッドの宣言が行われ、それを実装することでトレイトが利用できるようになります。
:型間の相互運用性と柔軟性: トレイトは型間での相互運用性や柔軟性を提供し、異なる型が同じ振る舞いを共有することができます。
}}
=== ジェネリックな実装 ===
; ジェネリックな構造体に対する実装
:<syntaxhighlight lang=rust copy>
struct MyGeneric<T> {
value: T,
}
impl<T> MyGeneric<T> {
fn new(value: T) -> MyGeneric<T> {
MyGeneric { value }
}
fn get_value(&self) -> &T {
&self.value
}
}
fn main() {
let instance = MyGeneric::new(42);
println!("Value: {:?}", instance.get_value());
let instance_str = MyGeneric::new("hello");
println!("Value: {:?}", instance_str.get_value());
}
</syntaxhighlight>
このコードは、<code>MyGeneric</code>を<code>i32</code>型と<code>&str</code>型の両方でインスタンス化しています。ジェネリックなデータ構造体とそのジェネリックなメソッドを利用して、異なる型に対して同じメソッドを使用する様子を示しています。<code>println!</code>マクロ内の<code>{:?}</code>は、<code>Debug</code>トレイトを実装する型の値を表示するためのフォーマット指定子です。
=== 他の例 ===
; 単純なトレイトの実装
:<syntaxhighlight lang=rust copy>
trait Summary {
fn summarize(&self) -> String;
}
struct Book {
title: String,
author: String,
}
impl Summary for Book {
fn summarize(&self) -> String {
format!("{} by {}", self.title, self.author)
}
}
fn summarize_any(item: &impl Summary) -> String {
item.summarize()
}
fn main() {
let book = Book {
title: String::from("Harry Potter"),
author: String::from("J.K. Rowling"),
};
println!("Summary: {}", summarize_any(&book));
}
</syntaxhighlight>
この例では、<code>Summary</code>トレイトを実装した様々な型に対して共通の<code>summary_any</code>関数を使用して、異なる型の値に対して要約を取得できます。
== 関数 ==
Rustでの関数は、プログラム内で再利用可能なコードブロックを定義するために使われます。関数は特定のタスクを実行するための手続きを含み、必要に応じて引数を受け取り、結果を返すことができます。基本的な関数の定義と使用方法を以下に示します。
=== 関数の定義 ===
:<syntaxhighlight lang=rust copy>
// 関数の定義
fn add(a: i32, b: i32) -> i32 {
a + b // 最後の式の結果が自動的に返り値になる
}
</syntaxhighlight>
この例では、<code>add</code>という名前の関数が定義されています。<code>a</code>と<code>b</code>は整数型(<code>i32</code>)の引数を受け取ります。<code>-> i32</code>は関数が<code>i32</code>型の値を返すことを示しています。
=== 関数の呼び出し ===
:<syntaxhighlight lang=rust copy>
let result = add(3, 5);
println!("Result: {}", result); // "Result: 8"が出力される
</syntaxhighlight>
<code>add</code>関数は<code>3</code>と<code>5</code>を引数に取り、それらを足して<code>8</code>を返します。<code>println!</code>マクロを使ってその結果を出力しています。
=== 引数と戻り値 ===
* 引数: 関数に渡す値。関数の定義において、引数は型を指定する必要があります。
* 戻り値: <code>-></code>演算子を使って関数が返す値の型を指定します。Rustでは最後の式の結果が自動的に返り値となります。
=== パターンマッチングを使用した複数の戻り値 ===
Rustの関数は複数の値を返すこともできます。
:<syntaxhighlight lang=rust copy>
fn calculate(a: i32, b: i32) -> (i32, i32) {
(a + b, a - b)
}
let (sum, difference) = calculate(10, 5);
println!("Sum: {}, Difference: {}", sum, difference); // "Sum: 15, Difference: 5"が出力される
</syntaxhighlight>
=== 関数の機能 ===
* 再帰: 自分自身を呼び出すことができます。
* クロージャ: 無名の関数を作成し、変数にキャプチャさせることができます。
* ジェネリクス: 型を指定せずに関数を定義することができ、後から具体的な型を指定できます。
Rustの関数は安全性、速度、パターンマッチング、ジェネリクス、所有権など、言語の多くの特徴を活用しています。これらの特性は、Rustを強力なプログラミング言語にしています。
=== ライフタイム: ===
Rustの関数とライフタイムは、関連性がありますが、関数のシグネチャ内でライフタイムを使用することは必ずしも必要ではありません。しかし、関数が参照を含む場合や、ジェネリクスを使う場合には、ライフタイムの指定が必要になることがあります。
==== 関数内のライフタイム ====
:<syntaxhighlight lang=rust copy>
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
</syntaxhighlight>
この例では、<code>longest</code>関数は2つの文字列スライスを引数として受け取り、それらのうち長さが長い方の参照を返します。<code>'a</code>はライフタイムパラメータであり、2つの引数と返り値の参照のライフタイムが同じことを示しています。これにより、返される参照が有効なスコープを保証します。
==== ライフタイムの省略 ====
Rustでは、ライフタイムの省略規則があります。特定のパターンにおいては、コンパイラが暗黙的にライフタイムを推論することができます。例えば、次のような関数シグネチャではライフタイムの省略が行われます。
:<syntaxhighlight lang=rust copy>
fn longest(x: &str, y: &str) -> &str {
// ...
}
</syntaxhighlight>
このような場合、コンパイラは適切なライフタイムを自動的に推論します。ただし、ライフタイムの省略は特定の条件に限定され、全ての場面で使えるわけではありません。
ライフタイムは主に、参照の有効期間を指定するために使用され、特に関数内で参照を扱う際に重要な役割を果たします。関数が複数の参照を扱い、それらの有効期間を整理する必要がある場合には、ライフタイムの指定が必要になることがあります。
=== クロージャ: ===
Rustではクロージャも関数として扱われます。クロージャは自身のスコープ外の変数をキャプチャして利用できます。これは非常に便利で、関数よりも柔軟な振る舞いを提供します。
次のコード例では、クロージャと関数の組み合わせを使って、外部スコープの変数をキャプチャして利用する方法を示します。
:<syntaxhighlight lang=rust copy>
fn main() {
let base_number = 10;
// クロージャの定義
let add_to_base = |x| x + base_number;
let new_number = 7;
// クロージャの使用
let result = add_to_base(new_number);
println!("Result: {}", result); // "Result: 17"が出力される
}
</syntaxhighlight>
この例では、<code>base_number</code>という変数が<code>add_to_base</code>クロージャにキャプチャされ、後でクロージャ内で使用されています。クロージャは外部の変数をキャプチャすることができ、そのコンテキストを保持して使用できる点が関数とは異なる特徴です。
Rustの関数は、パターンマッチングやジェネリクス、ライフタイム、クロージャなどの機能と組み合わせて、安全で効率的なコードを記述するための強力なツールです。
== 高階関数 ==
Rustは、関数型プログラミングの要素を備えた汎用プログラミング言語です。Rustでは、関数を変数やデータ構造に格納したり、関数を関数のパラメータとして渡したりすることができます。これらの機能は、高階関数として知られています。
Rustで高階関数を使用すると、コードの再利用性と保守性を向上させることができます。また、コードをより簡潔で読みやすくすることもできます。
Rustで高階関数を使用する方法はいくつかあります。
=== 関数を変数に格納する ===
Rustでは、関数を変数に格納することができます。これにより、関数を繰り返し使用したり、関数を別の関数に渡したりすることができます。
:<syntaxhighlight lang=rust copy>
fn square(x: i32) -> i32 {
x * x
}
let square_function = square;
println!("The square of 5 is {}", square_function(5));
</syntaxhighlight>
このコードでは、square()関数をsquare_function変数に格納しています。その後、square_function()関数を呼び出すことで、square()関数と同じ結果を得ることができます。
=== 関数を関数のパラメータとして渡す ===
Rustでは、関数を関数のパラメータとして渡すことができます。これにより、関数を別の関数に処理させることができます。
:<syntaxhighlight lang=rust copy>
fn map_numbers(numbers: &[i32], f: fn(i32) -> i32) -> Vec<i32> {
let mut result = Vec::new();
for number in numbers {
result.push(f(number));
}
result
}
fn square(x: i32) -> i32 {
x * x
}
let numbers = [1, 2, 3, 4, 5];
let squared_numbers = map_numbers(numbers, square);
println!("The squared numbers are: {:?}", squared_numbers);
</syntaxhighlight>
このコードでは、map_numbers()関数は、numbers配列の各要素をf関数に渡し、その結果を新しい配列に格納します。
=== クロージャを使用する ===
Rustでは、クロージャを使用して高階関数を作成することができます。クロージャは、関数本体の一部として定義された関数です。クロージャは、変数やデータ構造を捕捉して、関数本体内で使用することができます。
:<syntaxhighlight lang=rust copy>
fn filter_numbers(numbers: &[i32], f: impl Fn(i32) -> bool) -> Vec<i32> {
let mut result = Vec::new();
for number in numbers {
if f(number) {
result.push(number);
}
}
result
}
fn is_even(x: i32) -> bool {
x % 2 == 0
}
let numbers = [1, 2, 3, 4, 5];
let even_numbers = filter_numbers(numbers, is_even);
println!("The even numbers are: {:?}", even_numbers);
</syntaxhighlight>
このコードでは、filter_numbers()関数は、numbers配列の各要素をfクロージャに渡し、その結果がtrueである場合、その要素を新しい配列に格納します。
Rustで高階関数を使用すると、コードの再利用性と保守性を向上させることができます。また、コードをより簡潔で読みやすくすることもできます。
== 標準ライブラリー ==
Rustの標準ライブラリー(Standard Library)は、Rustコンパイラにバンドルされており、基本的なデータ構造、OSとのやり取り、スレッド、ファイルI/O、ネットワーキングなどの多くの機能を提供しています。以下に、標準ライブラリーの主要なカテゴリを紹介します。
=== コレクション(Collections) ===
* <code>Vec</code>, <code>VecDeque</code>: ベクターや双方向キューなどの動的な配列。
* <code>HashMap</code>, <code>BTreeMap</code>: ハッシュマップやBツリーマップなどのキーと値のペアを保持するマップ。
* <code>HashSet</code>, <code>BTreeSet</code>: ハッシュセットやBツリーセットなどのユニークな値を保持するセット。
=== スレッドと同期(Concurrency) ===
* <code>std::thread</code>: スレッドの生成と操作を提供。
* <code>std::sync</code>: Mutex、Atomicなどの同期機能を提供するモジュール。
=== ファイルI/Oとネットワーキング(I/O and Networking) ===
* <code>std::fs</code>: ファイルシステムとの相互作用を可能にするモジュール。
* <code>std::net</code>: ネットワーキングのためのモジュール。
=== プリミティブ型(Primitive Types) ===
* <code>std::primitive</code>: Rustのプリミティブ型(整数、浮動小数点数など)の機能を提供。
=== OS相互作用とその他(OS Interactions and Miscellaneous) ===
* <code>std::env</code>: 環境変数の取得などのOS環境に関する操作。
* <code>std::time</code>: 時間に関する機能を提供。
=== 入出力(Input/Output) ===
* <code>std::io</code>: 標準入出力やバッファリング、ファイルI/OなどのI/O操作を提供。
=== 文字列処理(String Manipulation) ===
* <code>std::str</code>: 文字列の操作、変換、検索などの機能を提供。
=== メモリ管理(Memory Management) ===
* <code>std::alloc</code>: メモリの割り当てと解放のための機能。
=== コンパイラ支援(Compiler Support) ===
* <code>std::marker</code>: マーカートレイトを提供し、コンパイラへのヒントを与える。
これらは標準ライブラリーの一部であり、Rustの基本的な機能を提供しています。開発者はこれらのモジュールや機能を組み合わせて、安全で効率的なプログラムを構築できます。
== コードギャラリー ==
このコードギャラリーは、さまざまなRustの機能やパターン、ベストプラクティスを示すためのサンプルコード集です。
=== エラトステネスの篩 ===
:<syntaxhighlight lang=rust copy>
fn eratosthenes(n: usize) {
let mut sieve = vec![true; n + 1];
sieve[0] = false;
sieve[1] = false;
for i in 2..=n {
if sieve[i] {
// jをi*iから始める
for j in (i * i..=n).step_by(i) {
sieve[j] = false;
}
}
if i * i >= n {
break;
}
}
for i in 2..=n {
if sieve[i] {
println!("{}", i);
}
}
}
fn main() {
eratosthenes(100);
}
</syntaxhighlight>
このRustのコードは、エラトステネスの篩を使用して与えられた範囲内の素数を見つけるものです。
# <code>eratosthenes</code> 関数は、与えられた <code>n</code> までの素数を見つけるためのエラトステネスの篩アルゴリズムを実装しています。このアルゴリズムでは、最初に <code>n + 1</code> サイズの <code>sieve</code> というブール型の配列を作成します。この配列は、各インデックスが素数かどうかを示します。
# <code>sieve[0]</code> と <code>sieve[1]</code> は素数ではないので、それらを <code>false</code> に設定します。
# 2 から <code>n</code> までの各数について、その数が素数である場合は、その数の倍数を素数ではないとマークします。これにより、素数の倍数を持つ数は素数ではないことがわかります。
# <code>main</code> 関数では、<code>eratosthenes</code> 関数を呼び出し、100までの素数を見つけます。見つかった素数は画面に出力されます。
このアルゴリズムは素数を見つけるための効率的な方法の一つであり、与えられた範囲内の素数を見つけることができます。
=== 最大公約数と最小公倍数 ===
:<syntaxhighlight lang=rust copy>
fn gcd2(m: i32, n: i32) -> i32 {
if n == 0 {
m
} else {
gcd2(n, m % n)
}
}
fn gcd(ints: &[i32]) -> i32 {
ints.iter().cloned().fold(ints[0], gcd2)
}
fn lcm2(m: i32, n: i32) -> i32 {
m * n / gcd2(m, n)
}
fn lcm(ints: &[i32]) -> i32 {
ints.iter().cloned().fold(ints[0], lcm2)
}
fn main() {
println!("gcd2(30, 45) => {}", gcd2(30, 45));
println!("gcd(&[30, 72, 12]) => {}", gcd(&[30, 72, 12]));
println!("lcm2(30, 72) => {}", lcm2(30, 72));
println!("lcm(&[30, 42, 72]) => {}", lcm(&[30, 42, 72]));
}
</syntaxhighlight>
このコードは高階関数を利用しています。<code>fold</code>関数は特に重要で、与えられた配列内の要素に対して特定の操作を順番に適用することができます。
まず、<code>gcd</code>関数では、<code>ints</code>配列内の要素に対して<code>fold</code>関数を使って最大公約数(<code>gcd2</code>関数)を計算しています。<code>fold</code>は初期値として<code>ints[0]</code>を受け取り、各要素<code>value</code>に対して<code>gcd2</code>を適用し、次の要素に対して再帰的に<code>gcd2</code>を適用します。これにより、配列内のすべての要素の最大公約数が計算されます。
同様に、<code>lcm</code>関数も<code>fold</code>を利用しています。ここでは<code>lcm2</code>関数が利用され、各要素に対して最小公倍数を求めるための計算が行われます。
高階関数の利用により、配列内の要素に対して繰り返し処理を行う必要がある場合でも、シンプルで効率的なコードを書くことができます。
=== 二分法 ===
[[W:二分法|二分法]]
:<syntaxhighlight lang=rust copy>
fn bisection(low: f64, high: f64, f: impl Fn(f64) -> f64) -> f64 {
let x = (low + high) / 2.0;
let fx = f(x);
match () {
_ if (fx.abs() - 1.0e-10) < f64::EPSILON => x,
_ if fx < 0.0 => bisection(x, high, f),
_ => bisection(low, x, f),
}
}
fn main() {
let result1 = bisection(0.0, 3.0, |x| x - 1.0);
println!("{}", result1);
let result2 = bisection(0.0, 3.0, |x| x * x - 1.0);
println!("{}", result2);
}
</syntaxhighlight>
: [[旧課程(-2012年度)高等学校数学B/数値計算とコンピューター#2分法]]の例を Rust に移植しました。
このRustのコードは、二分法(bisection method)を使って与えられた関数の根を見つけるものです。
<code>bisection</code>関数は、<code>low</code>から<code>high</code>の範囲で与えられた関数 <code>f</code> の根を探します。<code>f</code> は <code>Fn(f64) -> f64</code> のトレイトを実装しており、実際の関数の定義は呼び出し時に与えられます。
この関数は再帰的に呼び出されます。与えられた区間 <code>[low, high]</code> の中央値 <code>x</code> を求め、その点での関数の値 <code>f(x)</code> を計算します。この値が非常に小さいか(ここでは <code>1.0e-10</code>未満)、または非常に近い数になるまで <code>low</code> または <code>high</code> を更新して、区間を狭めていきます。
<code>main</code>関数では、2つの異なる関数 <code>x - 1</code> と <code>x^2 - 1</code> に対して <code>bisection</code> 関数を呼び出して、それぞれの関数の根を探し、<code>println!</code> マクロを使って根を表示しています。
=== 構造体とメソッド ===
Rustにクラスはありませんが、構造体がメソッドを持つことが出来ます。
:<syntaxhighlight lang=rust copy>
#[derive(Debug)]
struct Hello {
s: String,
}
impl Hello {
fn new(s: &str) -> Hello {
// 空文字列の場合は "world" を使用
let s = if s.is_empty() { "world" } else { s };
Hello { s: s.to_string() }
}
fn to_string(&self) -> String {
format!("Hello {}!", self.s)
}
fn print(&self) {
println!("{}", self.to_string()); // 直接to_stringを呼び出す
}
}
fn main() {
let hello1 = Hello::new("");
hello1.print(); // to_string()を呼ぶ必要がなくなる
let hello2 = Hello::new("my friend");
hello2.print(); // 同上
println!(
"Hello.constructor.name => Hello\nhello1 => {:?}\nhello2.s => {}",
hello1, hello2.s
);
}
</syntaxhighlight>
このRustのコードは、<code>Hello</code>という名前の構造体を定義し、その構造体に関連するメソッドや、<code>main()</code>関数を含んでいます。
まず、<code>Hello</code>構造体は<code>String</code>型の<code>s</code>フィールドを持っています。<code>#[derive(Debug)]</code>アトリビュートは、この構造体に<code>Debug</code>トレイトを自動的に実装するようコンパイラに指示しています。<code>Debug</code>トレイトを実装することで、デバッグ目的で構造体の内容を出力できるようになります。
<code>impl Hello</code>ブロックでは、<code>Hello</code>構造体に対するメソッドが定義されています。
* <code>new</code>メソッドは、引数として文字列を受け取り、それが空文字列の場合はデフォルトの文字列 "world" を持つ<code>Hello</code>構造体を生成します。それ以外の場合は、引数で渡された文字列を使用して<code>Hello</code>構造体を作成します。
* <code>to_string</code>メソッドは、<code>Hello</code>構造体のインスタンスに対して、挨拶文を含む文字列を生成します。
* <code>print</code>メソッドは、<code>Hello</code>構造体のインスタンスの<code>s</code>フィールド(挨拶文)を標準出力に表示します。
<code>main()</code>関数では、<code>Hello</code>構造体を使ってインスタンスを生成し、メソッドを呼び出しています。<code>println!</code>マクロを使用して、構造体やそのフィールドをデバッグ出力しています。最後の<code>println!</code>マクロでは、<code>hello1</code>のデバッグ表示(<code>{:?}</code>)と<code>hello2</code>の<code>s</code>フィールドを出力しています。
=== 構造体とメソッド(2) ===
[[Go/メソッドとインターフェース]]からの移植です。
:<syntaxhighlight lang=go>
use std::f64::consts::PI;
#[derive(Debug)]
struct GeoCoord {
longitude: f64,
latitude: f64,
}
impl GeoCoord {
/// 地球の半径(キロメートル)
const EARTH_RADIUS: f64 = 6371.008;
/// 緯度・経度をラジアンに変換するための係数
const RADIAN_CONVERSION: f64 = PI / 180.0;
/// 2つの地理座標間の距離を計算する
fn distance(&self, other: &GeoCoord) -> f64 {
let lat_i = self.latitude * Self::RADIAN_CONVERSION;
let other_lat_i = other.latitude * Self::RADIAN_CONVERSION;
let long_diff_i = (self.longitude - other.longitude) * Self::RADIAN_CONVERSION;
let sin_lat = f64::sin(lat_i) * f64::sin(other_lat_i);
let cos_lat = f64::cos(lat_i) * f64::cos(other_lat_i);
let cos_long_diff = f64::cos(long_diff_i);
let distance = f64::acos(sin_lat + cos_lat * cos_long_diff) * Self::EARTH_RADIUS;
distance
}
}
impl std::fmt::Display for GeoCoord {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let (ew, ns, long, lat) = format_coordinates(self.longitude, self.latitude);
write!(f, "({}: {:.6}, {}: {:.6})", ew, long, ns, lat)
}
}
/// 緯度・経度の値を適切にフォーマットする関数
fn format_coordinates(longitude: f64, latitude: f64) -> (&'static str, &'static str, f64, f64) {
let (ew, long) = if longitude < 0.0 {
("西経", -longitude)
} else {
("東経", longitude)
};
let (ns, lat) = if latitude < 0.0 {
("南緯", -latitude)
} else {
("北緯", latitude)
};
(ew, ns, long, lat)
}
fn main() {
let sites = vec![
("東京駅", GeoCoord { longitude: 139.7673068, latitude: 35.6809591 }),
("シドニー・オペラハウス", GeoCoord { longitude: 151.215278, latitude: -33.856778 }),
("グリニッジ天文台", GeoCoord { longitude: -0.0014, latitude: 51.4778 }),
];
for (name, gc) in &sites {
println!("{}: {}", name, gc);
}
for i in 0..sites.len() {
let current_site = &sites[i];
let next_site = &sites[(i + 1) % sites.len()];
println!(
"{} - {}: {:.2} [km]",
current_site.0,
next_site.0,
current_site.1.distance(&next_site.1)
);
}
}
</syntaxhighlight>
=== 逆ポーランド記法の解析と評価 ===
{{先頭に戻る|title=コード・ギャラリーに戻る|label=コードギャラリー|style=border-top:1px solid gray;}}
逆ポーランド記法は、数式の演算子を後置記法で表現する方法です。通常の中置記法では演算子がオペランドの間に置かれますが、逆ポーランド記法では演算子がオペランドの後ろに置かれます。これにより、括弧や演算子の優先順位を考える必要がなくなり、計算機で容易に評価できる形式になります。
例えば、中置記法での式 <code>3 + 4 * 5</code> は、逆ポーランド記法では <code>3 4 5 * +</code> と表現されます。この記法では、演算子が対象のオペランドに対して順番に適用されます。
:<syntaxhighlight lang=rust copy>
enum Token {
Add,
Sub,
Mul,
Div,
Operand(i32),
}
impl Token {
fn evaluate(&self, stack: &mut Vec<i32>) -> Result<(), &'static str> {
match self {
Token::Add | Token::Sub | Token::Mul | Token::Div => {
if stack.len() < 2 {
return Err("Invalid expression: not enough operands for operator");
}
let operand2 = stack.pop().unwrap();
let operand1 = stack.pop().unwrap();
match self {
Token::Add => stack.push(operand1 + operand2),
Token::Sub => stack.push(operand1 - operand2),
Token::Mul => stack.push(operand1 * operand2),
Token::Div => {
if operand2 == 0 {
return Err("Division by zero");
}
stack.push(operand1 / operand2);
}
_ => unreachable!(),
}
}
Token::Operand(num) => {
stack.push(*num);
}
}
Ok(())
}
}
fn evaluate_expression(expression: &str) -> Result<i32, &'static str> {
let mut stack: Vec<i32> = Vec::new();
let tokens: Vec<Token> = expression
.split_whitespace()
.map(|token| {
if let Ok(parsed_num) = token.parse::<i32>() {
Token::Operand(parsed_num)
} else {
match token {
"+" => Token::Add,
"-" => Token::Sub,
"*" => Token::Mul,
"/" => Token::Div,
_ => unreachable!(),
}
}
})
.collect();
for token in tokens {
if let Err(err) = token.evaluate(&mut stack) {
return Err(err);
}
}
if stack.len() != 1 {
return Err("Invalid expression: too many operands or operators");
}
Ok(stack[0])
}
fn main() {
let expression = "5 3 2 * + 8 2 / -";
match evaluate_expression(expression) {
Ok(result) => println!("Result: {}", result),
Err(err) => println!("Error: {}", err),
}
}
</syntaxhighlight>
このコードは、<code>Token</code>という列挙型を使って逆ポーランド記法の式を評価する関数を実装しています。
まず、<code>Token</code>は<code>Add</code>、<code>Sub</code>、<code>Mul</code>、<code>Div</code>、<code>Operand</code>の5つのバリアントを持ちます。<code>Operand</code>は整数値を保持します。
<code>Token</code>には<code>evaluate</code>というメソッドが実装されています。このメソッドでは、<code>Token</code>の各バリアントに対する処理が行われます。<code>Add</code>、<code>Sub</code>、<code>Mul</code>、<code>Div</code>の場合は、スタックから2つの値を取り出して、それらを演算し結果をスタックに積みます。<code>Operand</code>の場合は、その値をスタックに積みます。
<code>evaluate_expression</code>関数では、与えられた式をトークン化して<code>Token</code>のベクターに変換し、それぞれのトークンに対して<code>evaluate</code>メソッドを実行します。各トークンの評価においてエラーが発生した場合、そのエラーメッセージが直ちに返されます。最終的に、スタックに残った値が1つでない場合もエラーが返されます。
<code>main</code>関数では、<code>evaluate_expression</code>の結果に応じて結果を出力するか、エラーを表示します。これにより、逆ポーランド記法の式を評価し、正常な結果またはエラーメッセージを表示できます。
=== 式を逆ポーランド記法に変換する(手書き) ===
{{先頭に戻る|title=コード・ギャラリーに戻る|label=コードギャラリー|style=border-top:1px solid gray;}}
:<syntaxhighlight lang=rust copy>
#[derive(Debug, Clone, Copy)]
enum Token {
Number(i32),
Plus,
Minus,
Multiply,
Divide,
}
fn main() {
let input = "12+34*56/78";
let tokens = parse_input(input);
let rpn = infix_to_rpn(tokens);
println!("{:?}", rpn);
}
fn parse_input(input: &str) -> Vec<Token> {
let mut tokens = Vec::new();
let mut num = String::new();
for c in input.chars() {
match c {
'0'..='9' => num.push(c), // 数字を収集
'+' | '-' | '*' | '/' => {
if !num.is_empty() {
let n = num.parse().unwrap();
tokens.push(Token::Number(n)); // 数字をトークンに追加
num.clear(); // 数字をリセット
}
// 演算子をトークンに追加
match c {
'+' => tokens.push(Token::Plus),
'-' => tokens.push(Token::Minus),
'*' => tokens.push(Token::Multiply),
'/' => tokens.push(Token::Divide),
_ => unreachable!(),
}
}
_ => panic!("Invalid character in input!"), // 無効な文字の場合
}
}
// 最後の数値をトークンに追加
if !num.is_empty() {
let n = num.parse().unwrap();
tokens.push(Token::Number(n));
}
tokens
}
fn infix_to_rpn(tokens: Vec<Token>) -> Vec<Token> {
let mut rpn = Vec::new();
let mut stack = Vec::new();
for token in tokens {
match token {
Token::Number(_) => rpn.push(token), // 数字はそのままRPNに追加
Token::Plus | Token::Minus | Token::Multiply | Token::Divide => {
while let Some(&top) = stack.last() {
if precedence(&token) <= precedence(&top) {
rpn.push(stack.pop().unwrap()); // 優先順位が高い演算子を出力
} else {
break; // 自分より優先順位が低い演算子が来たら中断
}
}
stack.push(token); // 演算子をスタックに追加
}
}
}
// スタックに残った演算子を全てRPNに追加
while let Some(op) = stack.pop() {
rpn.push(op);
}
rpn
}
fn precedence(token: &Token) -> i32 {
match token {
Token::Multiply | Token::Divide => 2,
Token::Plus | Token::Minus => 1,
_ => 0,
}
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
[Number(12), Number(34), Number(56), Multiply, Number(78), Divide, Plus]
</syntaxhighlight>
このコードは、与えられた文字列を逆ポーランド記法(RPN)に変換するプログラムです。以下にその構造を解説します:
# <code>Token</code> 列挙型: 数字と演算子を表す列挙型です。<code>Number</code> は数字を、<code>Plus</code>, <code>Minus</code>, <code>Multiply</code>, <code>Divide</code> はそれぞれ演算子を表します。<code>derive(Debug, Clone, Copy)</code> が付与されており、デバッグ表示やクローン、コピーが可能です。
# <code>parse_input</code> 関数: 与えられた文字列をトークンに分割します。数字の場合は文字列を数値に変換して <code>Token::Number</code> に、演算子の場合は対応する <code>Token</code> に変換し、それらを <code>Vec<Token></code> に収集します。
# <code>infix_to_rpn</code> 関数: 中置記法のトークンのベクターを逆ポーランド記法に変換します。スタックとRPNベクターを使用して、トークンを処理します。演算子の場合、スタックのトップとの優先順位を比較して、適切な順序でRPNに追加します。
# <code>precedence</code> 関数: 演算子の優先順位を返します。乗算と除算が優先され、それ以外の演算子は同じ優先順位です。
このコードは、入力された文字列を数値と演算子に分割し、それらを逆ポーランド記法に変換する機能を持っています。特定の演算子の優先順位を考慮しながら適切な順序で演算子を配置し、RPNを生成します。
=== 式を逆ポーランド記法に変換する(手書き:別解:再帰下降パーサー) ===
{{先頭に戻る|title=コード・ギャラリーに戻る|label=コードギャラリー|style=border-top:1px solid gray;}}
:<syntaxhighlight lang=rust copy>
#[derive(Debug, Clone, PartialEq)] // Cloneトレイトを追加
enum Token {
Number(f64),
Plus,
Minus,
Multiply,
Divide,
LParen,
RParen,
EOF,
}
struct Lexer<'a> {
input: &'a str,
position: usize,
}
impl<'a> Lexer<'a> {
fn new(input: &'a str) -> Lexer<'a> {
Lexer { input, position: 0 }
}
// 空白をスキップする
fn skip_whitespace(&mut self) {
while self.position < self.input.len() && self.input.chars().nth(self.position).unwrap().is_whitespace() {
self.position += 1;
}
}
// 次のトークンを取得する
fn next_token(&mut self) -> Token {
self.skip_whitespace();
if self.position >= self.input.len() {
return Token::EOF;
}
let current_char = self.input.chars().nth(self.position).unwrap();
self.position += 1; // 次の位置に進める
match current_char {
'+' => Token::Plus,
'-' => Token::Minus,
'*' => Token::Multiply,
'/' => Token::Divide,
'(' => Token::LParen,
')' => Token::RParen,
_ if current_char.is_digit(10) || current_char == '.' => {
let start = self.position - 1; // トークンの開始位置
while self.position < self.input.len() && (self.input.chars().nth(self.position).unwrap().is_digit(10) || self.input.chars().nth(self.position).unwrap() == '.') {
self.position += 1;
}
let number_str = &self.input[start..self.position];
Token::Number(number_str.parse::<f64>().unwrap())
}
_ => panic!("Invalid character found: {}", current_char),
}
}
}
struct Parser<'a> {
lexer: Lexer<'a>,
current_token: Token,
}
impl<'a> Parser<'a> {
fn new(mut lexer: Lexer<'a>) -> Parser<'a> {
let current_token = lexer.next_token();
Parser { lexer, current_token }
}
// トークンを消費する
fn eat(&mut self, token: Token) {
if self.current_token == token {
self.current_token = self.lexer.next_token();
} else {
panic!("Invalid syntax");
}
}
// 項を解析する
fn factor(&mut self) -> Vec<Token> {
match self.current_token {
Token::Number(num) => {
self.eat(Token::Number(num));
vec![Token::Number(num)]
}
Token::LParen => {
self.eat(Token::LParen);
let result = self.expr();
self.eat(Token::RParen);
result
}
_ => panic!("Invalid syntax"),
}
}
// 積項を解析する
fn term(&mut self) -> Vec<Token> {
let mut result = self.factor();
while matches!(self.current_token, Token::Multiply | Token::Divide) {
let op = self.current_token.clone();
self.eat(op.clone());
let mut next_factor = self.factor();
result.append(&mut next_factor);
result.push(op);
}
result
}
// 式を解析する
fn expr(&mut self) -> Vec<Token> {
let mut result = self.term();
while matches!(self.current_token, Token::Plus | Token::Minus) {
let op = self.current_token.clone();
self.eat(op.clone());
let mut next_term = self.term();
result.append(&mut next_term);
result.push(op);
}
result
}
}
// 逆ポーランド記法を生成する関数
fn generate_reverse_polish_notation(input: &str) -> Vec<Token> {
let lexer = Lexer::new(input);
let mut parser = Parser::new(lexer);
parser.expr()
}
fn main() {
let result = generate_reverse_polish_notation("12 + 34 * 56 / 78");
println!("{:?}", result);
}
</syntaxhighlight>
コードは、与えられた数式を逆ポーランド記法に変換するためのプログラムです。ここでは、Lexer(字句解析器)とParser(構文解析器)という2つの主要な構成要素があります。
; Token
: <code>Token</code> 列挙型は、数式をトークンに分割するために使用されます。数字、演算子、および括弧のトークンを定義します。
; Lexer
: <code>Lexer</code> は、与えられた数式文字列をトークンに分割する役割を担います。<code>next_token</code> メソッドは、数式の文字列を走査して、各文字がどの種類のトークンに対応するかを判断します。
; Parser
: <code>Parser</code> は、Lexer によって生成されたトークンのストリームを受け取り、逆ポーランド表記に変換します。再帰的に式を解析し、優先順位を考慮しながら、逆ポーランド表記のトークン列を生成します。
:* <code>factor()</code> メソッドは、数または括弧で始まる要素(ファクター)を解析します。
:* <code>term()</code> メソッドは、乗算と除算の演算子を解析します。
:* <code>expr()</code> メソッドは、加算と減算の演算子を解析します。
; generate_reverse_polish_notation 関数
: この関数は、与えられた数式文字列を逆ポーランド表記に変換します。Lexer を使ってトークンに分割し、Parser を使って逆ポーランド表記のトークン列を生成します。
; main 関数
: <code>generate_reverse_polish_notation</code> を使って、指定された式を逆ポーランド表記で出力します。
逆ポーランド記法は、演算子がオペランドの後ろに置かれるので、式を解析してトークン列に変換することで、演算の優先順位を反映した形で数式を表現することができます。
----
これらのコードは、与えられた数学式を逆ポーランド記法(Reverse Polish Notation, RPN)に変換する方法を示していますが、アプローチが異なります。
1番目のコードは、文字列を直接解析してトークンに分割し、その後逆ポーランド記法に変換しています。一方、2番目のコードは、字句解析器(lexer)とパーサー(parser)を使用して、トークンに分割し、その後パースして逆ポーランド記法に変換しています。
1番目のコードは、基本的な数値と演算子の処理に集中しています。一方で、2番目のコードは字句解析や構文解析の段階を厳密に分離しています。また、2番目のコードは小数点もサポートしており、より柔軟な数値表現を可能にしています。
どちらのコードも同じ目的を果たしていますが、アプローチの違いが見られます。1番目のコードはシンプルで直感的ですが、拡張性に欠けるかもしれません。
一方で、2番目のコードはより複雑ですが、より柔軟で拡張性があります。それぞれのコードには長所と短所がありますが、どちらも与えられた数式を逆ポーランド記法に変換する点では同等の結果を提供します。実際には2番めのコードは不動小数点数やカッコに対応しています。
=== 式を逆ポーランド記法に変換する(nom版) ===
{{先頭に戻る|title=コード・ギャラリーに戻る|label=コードギャラリー|style=border-top:1px solid gray;}}
:<syntaxhighlight lang=rust copy>
use nom::character::complete::{char, digit1};
use nom::combinator::map;
use nom::multi::many0;
use nom::sequence::delimited;
use nom::IResult;
#[derive(Debug, Clone, Copy)]
enum Token {
Number(i32),
Plus,
Minus,
Multiply,
Divide,
}
fn main() {
let input = "12+34*56/78";
let (_, tokens) = parse_input(input).unwrap();
let rpn = infix_to_rpn(tokens);
println!("{:?}", rpn);
}
fn parse_input(input: &str) -> IResult<&str, Vec<Token>> {
many0(parse_token)(input)
}
fn parse_token(input: &str) -> IResult<&str, Token> {
let (input, token) = delimited(
nom::character::complete::space0,
nom::branch::alt((
map(digit1, |s: &str| Token::Number(s.parse().unwrap())),
map(char('+'), |_| Token::Plus),
map(char('-'), |_| Token::Minus),
map(char('*'), |_| Token::Multiply),
map(char('/'), |_| Token::Divide),
)),
nom::character::complete::space0,
)(input)?;
Ok((input, token))
}
fn infix_to_rpn(tokens: Vec<Token>) -> Vec<Token> {
let mut rpn = Vec::new();
let mut stack = Vec::new();
for token in tokens {
match token {
Token::Number(_) => rpn.push(token),
Token::Plus | Token::Minus | Token::Multiply | Token::Divide => {
while let Some(top) = stack.last().copied() {
if precedence(&token) <= precedence(&top) {
rpn.push(stack.pop().unwrap());
} else {
break;
}
}
stack.push(token);
}
}
}
while let Some(op) = stack.pop() {
rpn.push(op);
}
rpn
}
fn precedence(token: &Token) -> i32 {
match token {
Token::Multiply | Token::Divide => 2,
Token::Plus | Token::Minus => 1,
_ => 0,
}
}
</syntaxhighlight>
このコードは、<code>nom</code>というパーサーコンビネータライブラリを使用して、与えられた文字列を解析し、トークンに分割する機能を持っています。前のコードと比較してみましょう。
# <code>parse_input</code> 関数: <code>many0</code>コンビネータを使って、<code>parse_token</code>を繰り返し適用し、入力文字列をトークンのベクターに変換します。<code>IResult</code>型を返します。
# <code>parse_token</code> 関数: <code>delimited</code>コンビネータを使用してトークンの前後のスペースを処理し、与えられた文字列を様々なルールにマッチングさせます。数字、演算子それぞれのパースを行い、<code>Token</code>列挙型のトークンを返します。
# <code>infix_to_rpn</code> 関数: 前のコードと同じですが、与えられたトークンのベクターを逆ポーランド記法に変換する機能を持っています。
このコードは、<code>nom</code>を使ってトークン分割を行い、より柔軟なパースを可能にしています。<code>nom</code>を使用することで、トークンのパースやスペースの処理など、より複雑なルールを柔軟に記述できるようになります。
=== 複素数式評価器 ===
{{先頭に戻る|title=コード・ギャラリーに戻る|label=コードギャラリー|style=border-top:1px solid gray;}}
:<syntaxhighlight lang=rust copy>
extern crate num_complex;
use num_complex::Complex;
#[derive(Debug, PartialEq, Clone)]
enum Token {
Number(Complex<f64>),
Plus,
Minus,
Multiply,
Divide,
LParen,
RParen,
EOF,
}
struct Lexer<'a> {
input: &'a str,
position: usize,
}
impl<'a> Lexer<'a> {
fn new(input: &'a str) -> Lexer<'a> {
Lexer { input, position: 0 }
}
fn skip_whitespace(&mut self) {
while self.position < self.input.len() && self.input.chars().nth(self.position).unwrap().is_whitespace() {
self.position += 1;
}
}
fn next_token(&mut self) -> Token {
self.skip_whitespace();
if self.position >= self.input.len() {
return Token::EOF;
}
let current_char = self.input.chars().nth(self.position).unwrap();
match current_char {
'+' => {
self.position += 1;
Token::Plus
}
'-' => {
self.position += 1;
Token::Minus
}
'*' => {
self.position += 1;
Token::Multiply
}
'/' => {
self.position += 1;
Token::Divide
}
'(' => {
self.position += 1;
Token::LParen
}
')' => {
self.position += 1;
Token::RParen
}
'i' => {
self.position += 1;
Token::Number(Complex::new(0.0, 1.0))
}
_ if current_char.is_digit(10) || current_char == '.' => {
let start = self.position;
while self.position < self.input.len()
&& (self.input.chars().nth(self.position).unwrap().is_digit(10)
|| self.input.chars().nth(self.position).unwrap() == '.')
{
self.position += 1;
}
let number_str = &self.input[start..self.position];
let number = number_str.parse::<f64>().unwrap();
if let Some('i') = self.input.chars().nth(self.position) {
self.position += 1;
Token::Number(Complex::new(0.0, number))
} else {
Token::Number(Complex::new(number, 0.0))
}
}
_ => panic!("Invalid character found: {}", current_char),
}
}
}
struct Parser<'a> {
lexer: Lexer<'a>,
current_token: Token,
}
impl<'a> Parser<'a> {
fn new(mut lexer: Lexer<'a>) -> Parser<'a> {
let current_token = lexer.next_token();
Parser { lexer, current_token }
}
fn eat(&mut self, token: Token) {
if self.current_token == token {
self.current_token = self.lexer.next_token();
} else {
panic!("Invalid syntax");
}
}
fn factor(&mut self) -> Complex<f64> {
let token = self.current_token.clone();
match token {
Token::Number(num) => {
self.eat(Token::Number(num));
num
}
Token::LParen => {
self.eat(Token::LParen);
let result = self.expr();
self.eat(Token::RParen);
result
}
_ => panic!("Invalid syntax"),
}
}
fn term(&mut self) -> Complex<f64> {
let mut result = self.factor();
while vec![Token::Multiply, Token::Divide].contains(&self.current_token) {
let token = self.current_token.clone();
match token {
Token::Multiply => {
self.eat(Token::Multiply);
let next_factor = self.factor();
result = result * next_factor;
}
Token::Divide => {
self.eat(Token::Divide);
let next_factor = self.factor();
result = result / next_factor;
}
_ => panic!("Invalid syntax"),
}
}
result
}
fn expr(&mut self) -> Complex<f64> {
let mut result = self.term();
while vec![Token::Plus, Token::Minus].contains(&self.current_token) {
let token = self.current_token.clone();
match token {
Token::Plus => {
self.eat(Token::Plus);
let next_term = self.term();
result = result + next_term;
}
Token::Minus => {
self.eat(Token::Minus);
let next_term = self.term();
result = result - next_term;
}
_ => panic!("Invalid syntax"),
}
}
result
}
}
fn main() {
let lexer = Lexer::new("(2+ 3i)*4i");
let mut parser = Parser::new(lexer);
let result = parser.expr();
println!("{:?}", result);
}
</syntaxhighlight>
このコードは、数式をパースして複素数を計算する簡単な計算機の基本的な実装です。Rustの機能を活用して、トークン列を生成するLexerと、そのトークン列を解析して計算を行うParserを定義しています。
<code>Token</code>は、パーサーが認識するトークンの種類を表すenumです。<code>Lexer</code>は文字列を受け取り、その文字列をトークンに分割する役割を果たします。各トークンは、演算子や数値、括弧などを表現しています。<code>Parser</code>は、Lexerが生成したトークン列を受け取り、それを解析して数式を計算します。
<code>Lexer</code>は空白をスキップし、文字列を一文字ずつ見ていき、トークン列を生成します。<code>Parser</code>はトークン列を再帰的に解析し、四則演算を行って複素数を計算します。演算子の優先順位や括弧の処理も考慮されています。
このコードは、入力文字列 <code>(2+ 3i)*4i</code> を受け取り、それを計算して結果を表示します。各段階でトークンが正しく識別され、演算子や数値が正しく解釈されることを期待しています。
コード内のパニックは、予期しないトークンや構文エラーがあった場合に発生します。これらのエラーは、コードが期待する形式に文字列が合致しなかった場合に発生します。
このコードを用いると、複雑な数式も計算できますが、入力の検証やエラー処理についてはまだ改善の余地があります。
=== 複素数 ===
:<syntaxhighlight lang=rust copy>
extern crate num_complex;
use num_complex::Complex;
fn main() {
// 複素数の作成
let a = Complex::new(3.0, 4.0);
let b = Complex::new(-2.0, 5.0);
println!("a: {a}");
println!("b: {b}");
println!("a + b: {}", a + b);
println!("a - b: {}", a - b);
println!("a * b: {}", a * b);
println!("a / b: {}", a / b);
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
a: 3+4i
b: -2+5i
a + b: 1+9i
a - b: 5-1i
a * b: -26+7i
a / b: 0.4827586206896552-0.7931034482758621i
</syntaxhighlight>
このコードは、<code>num-complex</code>クレートを使用して複素数を扱うRustの例です。
# <code>extern crate num_complex;</code> および <code>use num_complex::Complex;</code> は、<code>num-complex</code>クレートから<code>Complex</code>型を使えるようにするためのインポートです。
# <code>Complex::new(3.0, 4.0);</code> および <code>Complex::new(-2.0, 5.0);</code> は、実部と虚部を指定して複素数を作成しています。
# <code>println!("a: {a}");</code> および <code>println!("b: {b}");</code> は、複素数 <code>a</code> と <code>b</code> を出力しています。
b: {}", a * b);</code>、<code>println!("a / b: {}", a / b);</code> は、それぞれ複素数 <code>a</code> と <code>b</code> の加算、減算、乗算、除算を行っています。結果を文字列として出力しています。
修正されたコードは次のようになります:
== 改廃された技術 ==
Rustの改廃された技術や利用が推奨されない技術は、言語標準の進化、安全性の向上、エコシステムの成熟などによって置き換えられます。以下に、代表的な技術を示します。
=== try! マクロ ===
* '''サポート開始年:''' 2015年
* '''サポート終了年:''' 2018年(非推奨化)
; 廃止または衰退の理由
: より簡潔で表現力のある<code>?</code>演算子が導入され、エラーハンドリングがより簡単になりました。
; 代替技術
: <code>?</code>演算子の使用が推奨されます。
=== std::sync::Arc::get_mut_unchecked ===
* '''サポート開始年:''' 2015年
* '''サポート終了年:''' 2018年(非推奨化)
; 利用推奨されない理由
: 安全性の問題があり、データ競合を引き起こす可能性があります。
; 代替技術
: <code>get_mut()</code>または<code>make_mut()</code>の使用が推奨されます。
=== std::env::home_dir ===
* '''対象:''' ホームディレクトリのパス取得
* '''サポート終了年:''' 2019年(非推奨化)
; 廃止または衰退の理由
: プラットフォーム間での一貫性の問題と、エッジケースでの不正確な結果。
; 代替技術
: <code>dirs</code>クレートや<code>home</code>クレートの使用が推奨されます。
=== catch_unwind_safe ===
* '''サポート開始年:''' 2016年
* '''サポート終了年:''' 2018年(非推奨化)
; 廃止または衰退の理由
: 安全性の保証に関する問題と、より良い代替手段の存在。
; 代替技術
: <code>std::panic::catch_unwind</code>と適切なパニック安全性の設計が推奨されます。
=== std::error::Error::description ===
* '''サポート開始年:''' 2015年
* '''サポート終了年:''' 2019年(非推奨化)
; 廃止または衰退の理由
: エラーメッセージの国際化や詳細な文脈提供が困難でした。
; 代替技術
: Display実装とError::sourceメソッドの使用が推奨されます。
=== 古いRaw Pointersの特定の使用法 ===
* '''対象:''' 特定の生ポインタ操作
* '''サポート終了年:''' 継続的に改善
; 廃止または衰退の理由
: メモリ安全性とプログラムの正確性に関する問題。
; 代替技術
: 参照、Box、Rc、Arcなどの安全な所有権型の使用が推奨されます。
=== std::thread::scoped ===
* '''サポート開始年:''' 2015年
* '''サポート終了年:''' 2015年(早期に削除)
; 廃止または衰退の理由
: メモリ安全性の問題が発見され、より安全な代替手段が必要とされました。
; 代替技術
: <code>crossbeam::scope</code>や<code>rayon</code>のスコープ付き並列処理が推奨されます。
=== 古いマクロ定義構文 ===
* '''サポート開始年:''' 2015年
* '''サポート終了年:''' なし(ただし新構文推奨)
; 廃止または衰退の理由
: 可読性とデバッグの困難さ、より表現力のある新しい構文の登場。
; 代替技術
: <code>macro_rules!</code>の新しい構文やproc-macroの使用が推奨されます。
=== std::ascii::AsciiExt ===
* '''サポート開始年:''' 2015年
* '''サポート終了年:''' 2019年(非推奨化)
; 廃止または衰退の理由
: トレイトの設計が最適でなく、より良い代替手段が標準ライブラリに追加されました。
; 代替技術
: str、charの組み込みメソッドの使用が推奨されます。
=== 古いFuture実装 ===
* '''対象:''' futures 0.1系
* '''サポート終了年:''' なし(ただし非推奨)
; 廃止または衰退の理由
: async/await構文の導入により、より簡潔で理解しやすい非同期プログラミングが可能になりました。
; 代替技術
: futures 0.3系とasync/await構文の使用が推奨されます。
== ほかの言語からの移植例 ==
=== 順列・組合わせ ===
Goから[[Go/関数#順列・組合わせ|順列・組合わせ]]を移植
==== 順列 ====
;[https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=73612aa97bd4a1541a0313f38effde87 順列]:<syntaxhighlight lang=rust line copy>
fn permutation<T: Clone>(s: &[T], n: usize) -> Vec<Vec<T>> {
if s.is_empty() {
panic!("slice is nil");
}
if n == 1 {
let mut result = Vec::new();
for v in s.iter() {
result.push(vec![v.clone()]);
}
return result;
}
let mut result = Vec::new();
for (i, v) in s.iter().enumerate() {
let mut sf = Vec::new();
for (j, w) in s.iter().enumerate() {
if j != i {
sf.push(w.clone());
}
}
for w in permutation(&sf, n - 1) {
let mut v_w = vec![v.clone()];
v_w.extend_from_slice(&w);
result.push(v_w);
}
}
result
}
fn main() {
println!("{:?}", permutation(&[1, 2, 3], 1));
println!("{:?}", permutation(&[0, 1, 2], 2));
println!(
"{:?}",
permutation(&["abc".to_string(), "def".to_string(), "xyz".to_string()], 3)
);
println!("{:?}", permutation::<i32>(&[], 2));
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
[[1], [2], [3]]
[[0, 1], [0, 2], [1, 0], [1, 2], [2, 0], [2, 1]]
[["abc", "def", "xyz"], ["abc", "xyz", "def"], ["def", "abc", "xyz"], ["def", "xyz", "abc"], ["xyz", "abc", "def"], ["xyz", "def", "abc"]]
</syntaxhighlight>
;解説
:上記の移植において、主に以下の点に注意が必要でした。
:* ジェネリック型の宣言方法がGoとは異なるため、<code>func Permutation[T any](s []T, n int)</code> のような書き方はできません。Rustでは、<code>fn permutation<T: Clone>(s: &[T], n: usize)</code> のように、<code><T></code>の前に<code>:</code>を付けてジェネリック境界を宣言します。
:* Goの<code>make</code>は、新しい配列やスライスを作成するための組み込み関数ですが、Rustでは<code>Vec::with_capacity()</code>や<code>Vec::new()</code>を使用します。
:* <code>panic!("slice is nil")</code>は、Rustのパニック処理において、エラーメッセージを伴うパニックを発生させるために使用されます。
:* <code>Vec</code>に要素を追加するには、Goの<code>append</code>に相当するRustのメソッドである<code>Vec::push()</code>や、<code>Vec::extend_from_slice()</code>を使用します。また、<code>Vec</code>の要素数は、<code>len()</code>ではなく<code>len()</code>と<code>Vec::capacity()</code>の両方を使って取得する必要があります。
==== 組合わせ ====
;[https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=06e15597fcf3bdc585abeccd3edb9454 組合わせ]:<syntaxhighlight lang=rust line copy>
fn combination<T: Clone>(s: &[T], n: usize) -> Vec<Vec<T>> {
if s.is_empty() {
panic!("slice is empty");
}
if n == 1 {
let mut result = Vec::new();
for v in s {
result.push(vec![v.clone()]);
}
return result;
}
let mut result = Vec::new();
for i in 0..=(s.len() - n) {
let v = s[i].clone();
for w in combination(&s[i + 1..], n - 1) {
let mut res = vec![v.clone()];
res.extend(w);
result.push(res);
}
}
return result;
}
fn main() {
println!("{:?}", combination(&[1, 2, 3], 1));
println!("{:?}", combination(&[0, 1, 2], 2));
println!(
"{:?}",
combination(&["abc", "def", "xyz"], 3)
);
// println!("{:?}", combination(&[], 2)); 要素型が確定できない
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
[[1], [2], [3]]
[[0, 1], [0, 2], [1, 2]]
[["abc", "def", "xyz"]]
</syntaxhighlight>
;解説
:上記の移植において、主に以下の点に注意が必要でした。
:*Rustのジェネリック関数の型パラメータには制約が必要なため、<code>T</code>がクローン可能であることを示す<code>Clone</code>トレイトを指定する必要があります。
:*Goのスライスと異なり、Rustのスライスは要素数が0の場合にも安全であるため、<code>ErrNilSlice</code>に相当する処理は<code>slice.is_empty()</code>で判定することができます。
:*Goのスライスと異なり、Rustのスライスは範囲外アクセスがパニックを引き起こすため、再帰呼び出し時にはスライスの範囲を明示的に指定する必要があります。
{{See also|JavaScript/オブジェクト#順列を求めるメソッドを配列に追加する|JavaScript/オブジェクト#組合わせを求めるメソッドを配列に追加する}}
== 脚註 ==
<references />
== 外部リンク ==
{{Wikipedia|Rust (プログラミング言語)|Rust}}
* [https://www.rust-lang.org/ 公式サイト(英語)]
* [https://www.rust-lang.org/ja/ 公式サイト(日本語)]
* [https://doc.rust-jp.rs/book-ja/title-page.html The Rust Programming Language 日本語版]
* [https://doc.rust-lang.org/book/title-page.html The Rust Programming Language (英語版)]
== 参考文献 ==
* {{Cite
|author=Jim Blandy, Jason Orendorff
|title=プログラミングRust
|edition=第2版
|publisher=オライリージャパン
|isbn=978-4873119786
|date=2022年1月19日
}}
* {{Cite
|author=Jim Blandy, Jason Orendorff
|title=プログラミングRust
|edition=第1版
|publisher=オライリージャパン
|isbn=978-4873118550
|date=2018年8月10日
}}
[[Category:Rust|*]]
[[カテゴリ:プログラミング言語]]
{{NDC|007.64}}
espucelp1cpbb3hx1x0qs91tm5y7ylv
高校化学 無機化学まとめ
0
35588
264515
246447
2024-11-29T13:03:10Z
~2024-20470
85133
264515
wikitext
text/x-wiki
{{pathnav|高等学校の学習|高等学校理科|高等学校 化学|pagename=無幾化学まとめ|frame=1|small=1}}
高校化学で扱われる気体の発生、沈殿反応、イオン・化合物の色、などについてまとめた。
== 気体 ==
乾燥剤欄の×は、その気体と乾燥剤が反応してしまうことを示す。
{| class="wikitable"
|+ 気体の製法と性質
|-
!rowspan="3"|気体 !! rowspan="3"|化学反応式 !! rowspan="3"|反応様式 !! rowspan="3"|性質 !! colspan="5"|乾燥剤 !! rowspan="3"|捕集法
|-
! colspan="2"|酸性 !! 中性 !! colspan="2"|塩基性
|-
! 十酸化四燐 !! 濃硝酸 !! 塩化カルシウム !! 酸化カルシウム !! ソーダ石灰
|-
| 水素 || <chem>Zn + H2SO4 -> ZnSO4 + H2 ^</chem>(稀硫酸) || 酸化還元反応 || 中性 || ○ || ○ || ○ || ○ || ○ || 水上置換
|-
| 酸素 || <chem>2H2O2 -> 2H2O + O2 ^</chem>(触媒:MnO<sub>2</sub>)<br><chem>2KClO3 -> 2KCl + 3O2 ^</chem>(加熱、触媒:MnO<sub>2</sub>) || 酸化還元反応 || 中性 || ○ || ○ || ○ || ○ || ○ || 水上置換
|-
| オゾン || <chem>3O2 -> 2O3</chem> || 無声放電/光化学反応 || 中性 || ○ || ○ || ○ || ○ || ○ || -
|-
| 窒素 || <chem>NH4NO2 -> 2H2O + N2 ^</chem>(加熱) || 熱分解反応 || 中性 || ○ || ○ || ○ || ○ || ○ || 水上置換
|-
|塩素 || <chem>MnO2 + 4HCl -> MnCl2 + 2H2O + Cl2 ^</chem>(加熱)<br><chem>Ca(ClO)2.2H2O + 4HCl-> CaCl2 + 4H2O + 2Cl2 ^</chem> || 酸化還元反応 || 酸性 || ○ || ○ || ○ || × || × || 下方置換
|-
| 塩化水素 || <chem>NaCl + H2SO4 -> NaHSO4 + HCl ^</chem>(濃硫酸) || 不揮発性酸による揮発性酸の遊離反応 || 酸性 || ○ || ○ || ○ || × || × || 下方置換
|-
| フッ化水素 || <chem>CaF2 + H2SO4 -> CaSO4 + 2HF ^ </chem>(濃硫酸) || 酸化還元反応 || 酸性 || ○ || ○ || ○ || × || × || 下方置換
|-
| 硫化水素 || <chem>FeS + H2SO4 -> FeSO4 + H2S ^ </chem>(稀硫酸)<br><chem>FeS + 2HCl -> FeCl2 + H2S ^</chem>(稀塩酸) || 弱酸遊離反応 || 酸性 || ○ || × ※1 || ○ || × || × || 下方置換
|-
| アンモニア || <chem> 2NH4Cl + Ca(OH)2 -> CaCl2 + 2H2O + 2NH3 ^ </chem>(加熱)<br><chem>(NH4)2SO4 + 2NaOH -> Na2SO4 + 2H2O + 2NH3 ^</chem>(加熱) || 弱塩基遊離反応 || 塩基性 || × || × || × ※2 || ○ || ○ || 上方置換
|-
| rowspan="2"|二酸化硫黄 || <chem>Cu + 2H2SO4 -> CuSO4 + 2H2O + SO2 ^ </chem>(熱濃硫酸) || 酸化還元反応 || rowspan="2"|酸性 || rowspan="2"|○ || rowspan="2"|○ || rowspan="2"|○ || rowspan="2"|× || rowspan="2"|× || rowspan="2"|下方置換
|-
| <chem>NaHSO3 + H2SO4 -> NaHSO4 + H2O + SO2 ^</chem>(稀硫酸)<br><chem>2NaHSO3 + H2SO4 -> Na2SO4 + 2H2O + 2SO2 ^</chem>(稀硫酸) || 弱酸遊離反応
|-
| 一酸化窒素 || <chem>3Cu + 8HNO3 -> 3Cu(NO3)2 + 4H2O + 2NO ^</chem>(稀硝酸) || 酸化還元反応 || 中性 || ○ || ○ || ○ || ○ || ○ || 水上置換
|-
| 二酸化窒素 || <chem>Cu + 4HNO3 -> Cu(NO3)2 + 2H2O + 2NO2 ^</chem>(濃硝酸) || 酸化還元反応 || 酸性 || ○ || ○ || ○ || × || × || 下方置換
|-
| rowspan="3"|一酸化炭素 || <chem>R + nO2 -> x H2O + y CO2 + z CO ^</chem>※3 || 不完全燃焼 || rowspan="3"|中性 || rowspan="3"|○ || rowspan="3"|○ || rowspan="3"|○ || rowspan="3"|○ || rowspan="3"|○ || rowspan="3"|水上置換
|-
| <chem>HCOOH -> H2O + CO ^</chem>(加熱) || 脱水反応
|-
| <chem>CO2 + C -> 2CO</chem><br><chem>C + H2O -> H2 + CO ^</chem> || 酸化還元反応
|-
| rowspan="2"|二酸化炭素 || <chem>CaCO3 + 2HCl -> CaCl2 + H2O + CO2 ^ </chem> || 弱酸遊離反応 || rowspan="2"|酸性 || rowspan="2"|○ || rowspan="2"|○ || rowspan="2"|○ || rowspan="2"|× || rowspan="2"|× || rowspan="2"|下方置換
|-
| <chem>2NaHCO3 -> Na2CO3 + H2O + CO2</chem> || 熱分解反応
|}
※1:濃硫酸は硫化水素と酸化還元反応を起こして単体の硫黄を生成する。
:<chem>H2SO4 + H2S -> S + SO2 + 2H2O</chem>
※2:アンモニアは塩化カルシウムと付加反応を起こして塩化カルシウム八アンモニア付加物を生成する。
:<chem>8NH3 + CaCl2 -> CaCl2.8(NH3)</chem>
※3:Rは有機化合物を表す。ここでは、炭素の単体も含む。
== 沈殿 ==
* 硝酸イオン <chem>NO3^-</chem>
: 沈殿しにくい
* 塩化物イオン <chem>Cl-</chem>
: <chem>Ag^+,Pb^2+,Hg2^2+</chem>と沈殿をつくる。<chem>AgCl</chem> は光で分解、<chem>PbCl2</chem> は熱湯に溶ける。
* 硫酸イオン <chem>SO4^2-</chem>
: <chem>Ba^2+,Ca^2+,Sr^2+,Pb^2+</chem>と沈殿をつくる。{{ruby|馬|Ba}}{{ruby|鹿|Ca}}に{{ruby|する|Sr}}{{ruby|な|Pb}}硫酸。
* 炭酸イオン <chem>CO3^2-</chem>
: アルカリ金属、<chem>NH4^+</chem>以外と沈殿をつくる。
* クロム酸イオン <chem>CrO4^2-</chem>
: <chem>Ba^2+,Pb^2+,Ag^+</chem>と沈殿をつくる。{{ruby|バ|Ba}}{{ruby|ナ|Pb}}ナを{{ruby|銀|Ag}}{{ruby|貨|褐}}で買ったら{{ruby|苦労|CrO4}}した。Ba,Pbの沈殿はバナナと同じ黄色。Agは赤褐色。
* 水酸化物イオン <chem>OH^-</chem>
: イオン化傾向で <chem> Li^+</chem> ~ <chem>Na^+</chem> 沈殿しにくい、<chem>Mg^2+</chem> ~ <chem> Cu^2+</chem> + <chem>Mn^2+</chem> 水酸化物が沈殿、<chem>Hg^2+,Ag^+</chem> 水酸化物が分解し酸化物が沈殿。水酸化物は加熱すると酸化物が生成する。<chem>Na^+</chem>までは<chem>NaOH</chem>水溶液が沈殿を作りにくいことから連想できる。<chem>Hg^2+,Ag^+</chem>は個別に覚え、その間のイオン化傾向の金属イオンは沈殿すると覚える。
* 硫化物イオン <chem>S^2-</chem>
: イオン化傾向で <chem> Li^+</chem> ~ <chem> Al^3+</chem> 沈殿しにくい、<chem>Mn^2+</chem> + <chem>Zn^2+</chem> ~ <chem>Ni^2+</chem> 中・塩基性で沈殿、<chem>Cd^2+</chem> + <chem>Sn^2+</chem> ~ <chem>Ag^+</chem> 沈殿する。{{ruby|あら|Al}}{{ruby|ま|Mn}}、{{ruby|あ|Zn}}{{ruby|に|Ni}}、た{{ruby|か|Cd}}{{ruby|す|Sn}}{{ruby|ぎ|Ag}}!
=== 錯イオン ===
水酸化ナトリウムの水溶液を過剰に加え、沈殿が錯イオンを形成し溶解するもの。両性金属 + <chem>Cr^3+</chem>。
<chem>Al(OH)3 ->[NaOH] [Al(OH)4]^-</chem>
<chem>Zn(OH)2 ->[NaOH] [Zn(OH)4]^2-</chem>
<chem>Sn(OH)2 ->[NaOH] [Zn(OH)4]^2-</chem>
<chem>Pb(OH)2 ->[NaOH] [Pb(OH)4]^2-</chem>
<chem>Cr(OH)3 ->[NaOH] [Cr(OH)4]^-</chem>
アンモニアの水溶液を過剰に加え、沈殿が錯イオンを形成し溶解するもの。<chem>Cu^2+,Cd^2+,Ag^+,Ni^2+,Zn^2+</chem>。{{ruby|どう|Cu}}{{ruby|か|Cd}}{{ruby|銀|Ag}}{{ruby|に|Ni}}{{ruby|会えん|Zn}}か。
<chem>Cu(OH)2 ->[NH3] [Cu(NH3)4]^2+</chem>
<chem>Ag2O ->[NH3] [Ag(NH3)2]^+</chem>
<chem>Ni(OH)2 ->[NH3] [Ni(NH3)6]^2+</chem>
<chem>Zn(OH)2 ->[NH3] [Zn(NH3)4]^2+</chem>
<chem>Cd(OH)2 ->[NH3] [Cd(NH3)4]^2+</chem>
これらの錯イオンは、ジアンミン銀(I)イオン、ヘキサアンミンニッケル(II)イオンを除いて配位数4である。
<chem>Fe^2+</chem> または <chem>Fe^3+</chem> を含む水溶液に、ヘキサシアニド鉄(II)酸カリウム <chem>K4[Fe(CN)6]</chem> 水溶液、ヘキサシアニド鉄(III)酸カリウム <chem>K3[Fe(CN)6]</chem> 水溶液、チアシオン酸カリウム <chem>KSCN</chem> 水溶液を加える。
{| class="wikitable"
|+
!
!<chem>Fe^2+</chem>
!<chem>Fe^3+</chem>
|-
|<chem>K4[Fe(CN)6]</chem>
|青白色沈殿
|濃青色沈殿
|-
|<chem>K3[Fe(CN)6]</chem>
|濃青色沈殿
|褐色溶液
|-
|<chem>KSCN</chem>
|変化なし
|血赤色溶液
|}
この内、青白色沈殿と褐色溶液になるものについては問われにくい。
== 色 ==
高校化学で扱われる化合物の色について扱う。
塩化物、硫酸塩、炭酸塩はすべて白。<!-- i.e. <chem>AgCl,PbCl2,Hg2Cl2,BaSO4,CaSO4,SrSO4,PbSO4,BaCO3</chem> -->
=== 水溶液中のイオン ===
<chem>Fe^2+</chem>:淡緑 <chem>Fe^3+</chem>:黄褐 <chem>Cu^2+</chem>:青 <chem>Ni^2+</chem>:緑 <chem>Cr^3+</chem>:緑 <chem>Mn^2+</chem>:淡桃 <chem>MnO4^-</chem>:赤紫 <chem>CrO4^2-</chem>:黄 <chem>Cr2O7^2-</chem>:赤橙 <chem>[Cu(NH3)4]^2+</chem>:深青 <chem>[Cr(OH)4]^-</chem>:濃緑 <chem>[Ni(NH3)6]^2+</chem>:青紫
[[ファイル:Дихромат калия.jpg|中央|サムネイル|Cr2O72-]]
[[ファイル:Potassium chromate sоlution.jpg|中央|サムネイル|CrO42-]]
[[ファイル:Synthesizing Copper Sulfate.jpg|中央|サムネイル|Cu2+]]
[[ファイル:Green rust from FeSO4.jpg|中央|サムネイル|Fe2+]]
=== 酸化物 ===
<chem>CuO</chem>:黒 <chem>Cu2O</chem>:赤 <chem>Fe2O3</chem>:赤褐 <chem>Fe3O4</chem>:黒 <chem>FeO</chem>:黒 <chem>Al2O3</chem>:白 <chem>Ag2O</chem>:褐色 <chem>ZnO</chem>:白 <chem>MnO2</chem>:黒 <chem>HgO</chem>:黄
[[ファイル:CopperIIoxide.jpg|中央|サムネイル|CuO]]
[[ファイル:CopperIoxide.jpg|中央|サムネイル|Cu2O]]
[[ファイル:Iron(III)-oxide-sample.jpg|中央|サムネイル|Fe2O3]]
[[ファイル:Fe3O4.JPG|中央|サムネイル|Fe3O4]]
[[ファイル:Iron(II) oxide.jpg|中央|サムネイル|FeO]]
[[ファイル:Oxid hlinitý.PNG|中央|サムネイル|Al2O3]]
[[ファイル:Zinc oxide sample.jpg|中央|サムネイル|ZnO]]
[[ファイル:Manganese(IV) oxide.jpg|中央|サムネイル|MnO4]]
[[ファイル:HgOpowder.jpg|中央|サムネイル|HgO]]
=== 水酸化物 ===
<chem>Fe(OH)2</chem>:緑白 <chem>Fe(OH)3</chem>:赤褐 <chem>Cu(OH)2</chem>:青白 <chem>Cr(OH)3</chem>:灰緑 <chem>Ni(OH)2</chem>:緑 その他:白
[[ファイル:Hydroxid železitý.PNG|中央|サムネイル|Fe(OH)3]]
[[ファイル:სპილენძის ჰიდროქსიდი.jpg|中央|サムネイル|Cu(OH)2]]
=== クロム酸塩 ===
<chem>BaCrO4</chem>:黄 <chem>PbCrO4</chem>:黄 <chem>Ag2CrO4</chem>:赤褐
=== 硫化物 ===
<chem>ZnS</chem> :白 <chem>CdS</chem>:黄 <chem>MnS</chem>:淡赤 <chem>SnS</chem>:褐 その他:黒
[[ファイル:Zinc_sulfide.jpg|中央|サムネイル|ZnS]]
[[ファイル:Cadmium_sulfide.jpg|中央|サムネイル|CdS]]
[[ファイル:Sulfid_manganatý.PNG|中央|サムネイル|MnS]]
=== 炎色反応 ===
{{ruby|リ|Li}}{{ruby|アカ|赤}}ー{{ruby|な|Na}}{{ruby|き|黄}}{{ruby|K|K}}{{ruby|村|紫}}{{ruby|動|Cu}}{{ruby|力|緑}}{{ruby|借る|Ca}}{{ruby|とう|橙}}{{ruby|する|Sr}}も{{ruby|くれない|紅}}{{ruby|馬|Ba}}{{ruby|力|緑}}
<gallery>
ファイル:Flametest--.swn.jpg|ガスバーナーの色
ファイル:FlammenfärbungLi.png|リチウム
ファイル:Flametest--Na.swn.jpg|ナトリウム
ファイル:FlammenfärbungK.png|カリウム
ファイル:FlammenfärbungCa.png|カルシウム
ファイル:FlammenfärbungSr.png|ストロンチウム
ファイル:Flametest--Cu.swn.jpg|銅
ファイル:BaCl Flame colour.jpg|バリウム
</gallery>
=== 気体 ===
* <chem>Cl2</chem> 黄緑
* <chem>NO2</chem> 赤褐
* <chem>O3</chem> 淡青
* <chem>F2</chem> 淡黄
== その他 ==
試薬の保存
:フッ化水素酸はガラスを溶かすのでポリエチレン容器に保存。
:黄リンは空気中で自然発火するので水中保存。
:硝酸は光化学反応で二酸化窒素に分解されるので褐色瓶中で冷暗所に保存。
:過酸化水素水はガラスに不純物として微量に含まれる金属を触媒とした分解反応で酸素を生じるのでポリエチレン容器に入れガス抜きの穴を開けた蓋をつけて保存。
両性金属:<chem>Al,\, Zn,\, Sn,\, Pb</chem> {{ruby|あ|Al}}{{ruby|あ|Zn}}{{ruby|すん|Sn}}{{ruby|な|Pb}}りと両性に愛される。
潮解性:<chem>NaOH,\, KOH,\, H3PO4,\, P4O10,\, CaCl2</chem>
風解性:<chem>Na2CO3.10H2O, \, CuSO4.5H2O</chem>
[[カテゴリ:無機化学]]
rd1xqx2th4hkdvwbrjo6923v4n213ma
264516
264515
2024-11-29T13:07:25Z
~2024-20470
85133
/* 気体 */
264516
wikitext
text/x-wiki
{{pathnav|高等学校の学習|高等学校理科|高等学校 化学|pagename=無幾化学まとめ|frame=1|small=1}}
高校化学で扱われる気体の発生、沈殿反応、イオン・化合物の色、などについてまとめた。
== 気体 ==
乾燥剤欄の×は、その気体と乾燥剤が反応してしまうことを示す。
{| class="wikitable"
|+ 気体の製法と性質
|-
!rowspan="3"|気体 !! rowspan="3"|化学反応式 !! rowspan="3"|反応様式 !! rowspan="3"|性質 !! colspan="5"|乾燥剤 !! rowspan="3"|捕集法
|-
! colspan="2"|酸性 !! 中性 !! colspan="2"|塩基性
|-
! 十酸化四燐 !! 濃硝酸 !! 塩化カルシウム !! 酸化カルシウム !! ソーダ石灰
|-
| 水素 || <chem>Zn + H2SO4 -> ZnSO4 + H2 ^</chem>(稀硫酸) || 酸化還元反応 || 中性 || ○ || ○ || ○ || ○ || ○ || 水上置換
|-
| 酸素 || <chem>2H2O2 -> 2H2O + O2 ^</chem>(触媒:MnO<sub>2</sub>)<br><chem>2KClO3 -> 2KCl + 3O2 ^</chem>(加熱、触媒:MnO<sub>2</sub>) || 酸化還元反応 || 中性 || ○ || ○ || ○ || ○ || ○ || 水上置換
|-
| オゾン || <chem>3O2 -> 2O3</chem> || 無声放電/光化学反応 || 中性 || ○ || ○ || ○ || ○ || ○ || -
|-
| 窒素 || <chem>NH4NO2 -> 2H2O + N2 ^</chem>(加熱) || 熱分解反応 || 中性 || ○ || ○ || ○ || ○ || ○ || 水上置換
|-
|塩素 || <chem>MnO2 + 4HCl -> MnCl2 + 2H2O + Cl2 ^</chem>(加熱)<br><chem>Ca(ClO)2.2H2O + 4HCl-> CaCl2 + 4H2O + 2Cl2 ^</chem> || 酸化還元反応 || 酸性 || ○ || ○ || ○ || × || × || 下方置換
|-
| 塩化水素 || <chem>NaCl + H2SO4 -> NaHSO4 + HCl ^</chem>(濃硫酸) || 不揮発性酸による揮発性酸の遊離反応 || 酸性 || ○ || ○ || ○ || × || × || 下方置換
|-
| フッ化水素 || <chem>CaF2 + H2SO4 -> CaSO4 + 2HF ^ </chem>(濃硫酸) || 酸化還元反応 || 酸性 || ○ || ○ || ○ || × || × || 下方置換
|-
| 硫化水素 || <chem>FeS + H2SO4 -> FeSO4 + H2S ^ </chem>(稀硫酸)<br><chem>FeS + 2HCl -> FeCl2 + H2S ^</chem>(稀塩酸) || 弱酸遊離反応 || 酸性 || ○ || × ※1 || ○ || × || × || 下方置換
|-
| アンモニア || <chem> 2NH4Cl + Ca(OH)2 -> CaCl2 + 2H2O + 2NH3 ^ </chem>(加熱)<br><chem>(NH4)2SO4 + 2NaOH -> Na2SO4 + 2H2O + 2NH3 ^</chem>(加熱) || 弱塩基遊離反応 || 塩基性 || × || × || × ※2 || ○ || ○ || 上方置換
|-
| rowspan="2"|二酸化硫黄 || <chem>Cu + 2H2SO4 -> CuSO4 + 2H2O + SO2 ^ </chem>(熱濃硫酸) || 酸化還元反応 || rowspan="2"|酸性 || rowspan="2"|○ || rowspan="2"|○ || rowspan="2"|○ || rowspan="2"|× || rowspan="2"|× || rowspan="2"|下方置換
|-
| <chem>NaHSO3 + H2SO4 -> NaHSO4 + H2O + SO2 ^</chem>(稀硫酸)<br><chem>2NaHSO3 + H2SO4 -> Na2SO4 + 2H2O + 2SO2 ^</chem>(稀硫酸) || 弱酸遊離反応
|-
| 一酸化窒素 || <chem>3Cu + 8HNO3 -> 3Cu(NO3)2 + 4H2O + 2NO ^</chem>(稀硝酸) || 酸化還元反応 || 中性 || ○ || ○ || ○ || ○ || ○ || 水上置換
|-
| 二酸化窒素 || <chem>Cu + 4HNO3 -> Cu(NO3)2 + 2H2O + 2NO2 ^</chem>(濃硝酸) || 酸化還元反応 || 酸性 || ○ || ○ || ○ || × || × || 下方置換
|-
| rowspan="3"|一酸化炭素 || <chem>R + nO2 -> x H2O + y CO2 + z CO ^</chem>※3 || 不完全燃焼 || rowspan="3"|中性 || rowspan="3"|○ || rowspan="3"|○ || rowspan="3"|○ || rowspan="3"|○ || rowspan="3"|○ || rowspan="3"|水上置換
|-
| <chem>HCOOH -> H2O + CO ^</chem>(加熱) || 脱水反応
|-
| <chem>CO2 + C -> 2CO</chem><br><chem>C + H2O -> H2 + CO ^</chem>(加熱) || 酸化還元反応
|-
| rowspan="2"|二酸化炭素 || <chem>CaCO3 + 2HCl -> CaCl2 + H2O + CO2 ^ </chem> || 弱酸遊離反応 || rowspan="2"|酸性 || rowspan="2"|○ || rowspan="2"|○ || rowspan="2"|○ || rowspan="2"|× || rowspan="2"|× || rowspan="2"|下方置換
|-
| <chem>2NaHCO3 -> Na2CO3 + H2O + CO2</chem> || 熱分解反応
|}
※1:濃硫酸は硫化水素と酸化還元反応を起こして単体の硫黄を生成する。
:<chem>H2SO4 + H2S -> S + SO2 + 2H2O</chem>
※2:アンモニアは塩化カルシウムと付加反応を起こして塩化カルシウム八アンモニア付加物を生成する。
:<chem>8NH3 + CaCl2 -> CaCl2.8(NH3)</chem>
※3:Rは有機化合物を表す。ここでは、炭素の単体も含む。
== 沈殿 ==
* 硝酸イオン <chem>NO3^-</chem>
: 沈殿しにくい
* 塩化物イオン <chem>Cl-</chem>
: <chem>Ag^+,Pb^2+,Hg2^2+</chem>と沈殿をつくる。<chem>AgCl</chem> は光で分解、<chem>PbCl2</chem> は熱湯に溶ける。
* 硫酸イオン <chem>SO4^2-</chem>
: <chem>Ba^2+,Ca^2+,Sr^2+,Pb^2+</chem>と沈殿をつくる。{{ruby|馬|Ba}}{{ruby|鹿|Ca}}に{{ruby|する|Sr}}{{ruby|な|Pb}}硫酸。
* 炭酸イオン <chem>CO3^2-</chem>
: アルカリ金属、<chem>NH4^+</chem>以外と沈殿をつくる。
* クロム酸イオン <chem>CrO4^2-</chem>
: <chem>Ba^2+,Pb^2+,Ag^+</chem>と沈殿をつくる。{{ruby|バ|Ba}}{{ruby|ナ|Pb}}ナを{{ruby|銀|Ag}}{{ruby|貨|褐}}で買ったら{{ruby|苦労|CrO4}}した。Ba,Pbの沈殿はバナナと同じ黄色。Agは赤褐色。
* 水酸化物イオン <chem>OH^-</chem>
: イオン化傾向で <chem> Li^+</chem> ~ <chem>Na^+</chem> 沈殿しにくい、<chem>Mg^2+</chem> ~ <chem> Cu^2+</chem> + <chem>Mn^2+</chem> 水酸化物が沈殿、<chem>Hg^2+,Ag^+</chem> 水酸化物が分解し酸化物が沈殿。水酸化物は加熱すると酸化物が生成する。<chem>Na^+</chem>までは<chem>NaOH</chem>水溶液が沈殿を作りにくいことから連想できる。<chem>Hg^2+,Ag^+</chem>は個別に覚え、その間のイオン化傾向の金属イオンは沈殿すると覚える。
* 硫化物イオン <chem>S^2-</chem>
: イオン化傾向で <chem> Li^+</chem> ~ <chem> Al^3+</chem> 沈殿しにくい、<chem>Mn^2+</chem> + <chem>Zn^2+</chem> ~ <chem>Ni^2+</chem> 中・塩基性で沈殿、<chem>Cd^2+</chem> + <chem>Sn^2+</chem> ~ <chem>Ag^+</chem> 沈殿する。{{ruby|あら|Al}}{{ruby|ま|Mn}}、{{ruby|あ|Zn}}{{ruby|に|Ni}}、た{{ruby|か|Cd}}{{ruby|す|Sn}}{{ruby|ぎ|Ag}}!
=== 錯イオン ===
水酸化ナトリウムの水溶液を過剰に加え、沈殿が錯イオンを形成し溶解するもの。両性金属 + <chem>Cr^3+</chem>。
<chem>Al(OH)3 ->[NaOH] [Al(OH)4]^-</chem>
<chem>Zn(OH)2 ->[NaOH] [Zn(OH)4]^2-</chem>
<chem>Sn(OH)2 ->[NaOH] [Zn(OH)4]^2-</chem>
<chem>Pb(OH)2 ->[NaOH] [Pb(OH)4]^2-</chem>
<chem>Cr(OH)3 ->[NaOH] [Cr(OH)4]^-</chem>
アンモニアの水溶液を過剰に加え、沈殿が錯イオンを形成し溶解するもの。<chem>Cu^2+,Cd^2+,Ag^+,Ni^2+,Zn^2+</chem>。{{ruby|どう|Cu}}{{ruby|か|Cd}}{{ruby|銀|Ag}}{{ruby|に|Ni}}{{ruby|会えん|Zn}}か。
<chem>Cu(OH)2 ->[NH3] [Cu(NH3)4]^2+</chem>
<chem>Ag2O ->[NH3] [Ag(NH3)2]^+</chem>
<chem>Ni(OH)2 ->[NH3] [Ni(NH3)6]^2+</chem>
<chem>Zn(OH)2 ->[NH3] [Zn(NH3)4]^2+</chem>
<chem>Cd(OH)2 ->[NH3] [Cd(NH3)4]^2+</chem>
これらの錯イオンは、ジアンミン銀(I)イオン、ヘキサアンミンニッケル(II)イオンを除いて配位数4である。
<chem>Fe^2+</chem> または <chem>Fe^3+</chem> を含む水溶液に、ヘキサシアニド鉄(II)酸カリウム <chem>K4[Fe(CN)6]</chem> 水溶液、ヘキサシアニド鉄(III)酸カリウム <chem>K3[Fe(CN)6]</chem> 水溶液、チアシオン酸カリウム <chem>KSCN</chem> 水溶液を加える。
{| class="wikitable"
|+
!
!<chem>Fe^2+</chem>
!<chem>Fe^3+</chem>
|-
|<chem>K4[Fe(CN)6]</chem>
|青白色沈殿
|濃青色沈殿
|-
|<chem>K3[Fe(CN)6]</chem>
|濃青色沈殿
|褐色溶液
|-
|<chem>KSCN</chem>
|変化なし
|血赤色溶液
|}
この内、青白色沈殿と褐色溶液になるものについては問われにくい。
== 色 ==
高校化学で扱われる化合物の色について扱う。
塩化物、硫酸塩、炭酸塩はすべて白。<!-- i.e. <chem>AgCl,PbCl2,Hg2Cl2,BaSO4,CaSO4,SrSO4,PbSO4,BaCO3</chem> -->
=== 水溶液中のイオン ===
<chem>Fe^2+</chem>:淡緑 <chem>Fe^3+</chem>:黄褐 <chem>Cu^2+</chem>:青 <chem>Ni^2+</chem>:緑 <chem>Cr^3+</chem>:緑 <chem>Mn^2+</chem>:淡桃 <chem>MnO4^-</chem>:赤紫 <chem>CrO4^2-</chem>:黄 <chem>Cr2O7^2-</chem>:赤橙 <chem>[Cu(NH3)4]^2+</chem>:深青 <chem>[Cr(OH)4]^-</chem>:濃緑 <chem>[Ni(NH3)6]^2+</chem>:青紫
[[ファイル:Дихромат калия.jpg|中央|サムネイル|Cr2O72-]]
[[ファイル:Potassium chromate sоlution.jpg|中央|サムネイル|CrO42-]]
[[ファイル:Synthesizing Copper Sulfate.jpg|中央|サムネイル|Cu2+]]
[[ファイル:Green rust from FeSO4.jpg|中央|サムネイル|Fe2+]]
=== 酸化物 ===
<chem>CuO</chem>:黒 <chem>Cu2O</chem>:赤 <chem>Fe2O3</chem>:赤褐 <chem>Fe3O4</chem>:黒 <chem>FeO</chem>:黒 <chem>Al2O3</chem>:白 <chem>Ag2O</chem>:褐色 <chem>ZnO</chem>:白 <chem>MnO2</chem>:黒 <chem>HgO</chem>:黄
[[ファイル:CopperIIoxide.jpg|中央|サムネイル|CuO]]
[[ファイル:CopperIoxide.jpg|中央|サムネイル|Cu2O]]
[[ファイル:Iron(III)-oxide-sample.jpg|中央|サムネイル|Fe2O3]]
[[ファイル:Fe3O4.JPG|中央|サムネイル|Fe3O4]]
[[ファイル:Iron(II) oxide.jpg|中央|サムネイル|FeO]]
[[ファイル:Oxid hlinitý.PNG|中央|サムネイル|Al2O3]]
[[ファイル:Zinc oxide sample.jpg|中央|サムネイル|ZnO]]
[[ファイル:Manganese(IV) oxide.jpg|中央|サムネイル|MnO4]]
[[ファイル:HgOpowder.jpg|中央|サムネイル|HgO]]
=== 水酸化物 ===
<chem>Fe(OH)2</chem>:緑白 <chem>Fe(OH)3</chem>:赤褐 <chem>Cu(OH)2</chem>:青白 <chem>Cr(OH)3</chem>:灰緑 <chem>Ni(OH)2</chem>:緑 その他:白
[[ファイル:Hydroxid železitý.PNG|中央|サムネイル|Fe(OH)3]]
[[ファイル:სპილენძის ჰიდროქსიდი.jpg|中央|サムネイル|Cu(OH)2]]
=== クロム酸塩 ===
<chem>BaCrO4</chem>:黄 <chem>PbCrO4</chem>:黄 <chem>Ag2CrO4</chem>:赤褐
=== 硫化物 ===
<chem>ZnS</chem> :白 <chem>CdS</chem>:黄 <chem>MnS</chem>:淡赤 <chem>SnS</chem>:褐 その他:黒
[[ファイル:Zinc_sulfide.jpg|中央|サムネイル|ZnS]]
[[ファイル:Cadmium_sulfide.jpg|中央|サムネイル|CdS]]
[[ファイル:Sulfid_manganatý.PNG|中央|サムネイル|MnS]]
=== 炎色反応 ===
{{ruby|リ|Li}}{{ruby|アカ|赤}}ー{{ruby|な|Na}}{{ruby|き|黄}}{{ruby|K|K}}{{ruby|村|紫}}{{ruby|動|Cu}}{{ruby|力|緑}}{{ruby|借る|Ca}}{{ruby|とう|橙}}{{ruby|する|Sr}}も{{ruby|くれない|紅}}{{ruby|馬|Ba}}{{ruby|力|緑}}
<gallery>
ファイル:Flametest--.swn.jpg|ガスバーナーの色
ファイル:FlammenfärbungLi.png|リチウム
ファイル:Flametest--Na.swn.jpg|ナトリウム
ファイル:FlammenfärbungK.png|カリウム
ファイル:FlammenfärbungCa.png|カルシウム
ファイル:FlammenfärbungSr.png|ストロンチウム
ファイル:Flametest--Cu.swn.jpg|銅
ファイル:BaCl Flame colour.jpg|バリウム
</gallery>
=== 気体 ===
* <chem>Cl2</chem> 黄緑
* <chem>NO2</chem> 赤褐
* <chem>O3</chem> 淡青
* <chem>F2</chem> 淡黄
== その他 ==
試薬の保存
:フッ化水素酸はガラスを溶かすのでポリエチレン容器に保存。
:黄リンは空気中で自然発火するので水中保存。
:硝酸は光化学反応で二酸化窒素に分解されるので褐色瓶中で冷暗所に保存。
:過酸化水素水はガラスに不純物として微量に含まれる金属を触媒とした分解反応で酸素を生じるのでポリエチレン容器に入れガス抜きの穴を開けた蓋をつけて保存。
両性金属:<chem>Al,\, Zn,\, Sn,\, Pb</chem> {{ruby|あ|Al}}{{ruby|あ|Zn}}{{ruby|すん|Sn}}{{ruby|な|Pb}}りと両性に愛される。
潮解性:<chem>NaOH,\, KOH,\, H3PO4,\, P4O10,\, CaCl2</chem>
風解性:<chem>Na2CO3.10H2O, \, CuSO4.5H2O</chem>
[[カテゴリ:無機化学]]
d7b2cocj1qdiyyw2gnvehxirc32zkli
高等学校歴史総合/冷戦終結後の紛争と平和への取り組み
0
36545
264524
264161
2024-11-29T22:57:21Z
Kwawe
68789
/* 紛争の解決と国際社会の役割 */ 過去執筆の事後処理(見直し・本文大幅修正)の第1段落が完了。
264524
wikitext
text/x-wiki
[[小学校・中学校・高等学校の学習]]>[[高等学校の学習]]>[[高等学校地理歴史]]>[[高等学校歴史総合]]>冷戦終結後の紛争と平和への取り組み
== 冷戦終結後の紛争 ==
== 紛争の解決と国際社会の役割 ==
1992年、湾岸戦争が始まると、日本も世界の平和のために協力するようになりました。そこで、国連平和維持活動等協力法(PKO協力法)を作り、カンボジアに自衛隊を送るようになりました。2001年のアメリカ同時多発テロ事件(9.11事件)からアメリカ軍のお手伝いをするために、海上自衛隊をインド洋へ送りました。また、イラクの復興を手伝うために自衛隊をイラクに送りました。
[[カテゴリ:高等学校歴史総合|れいせんしゆうけつこのふんそうとへいわへのとりくみ]]
[[カテゴリ:国際関係学]]
[[カテゴリ:20世紀]]
[[カテゴリ:21世紀]]
qmmpdwg4hfhz9tw6y1eekjm2rty4ynh
264525
264524
2024-11-29T22:57:48Z
Kwawe
68789
/* 紛争の解決と国際社会の役割 */ 過去執筆の事後処理(見直し・大幅修正)の第2段落が完了。
264525
wikitext
text/x-wiki
[[小学校・中学校・高等学校の学習]]>[[高等学校の学習]]>[[高等学校地理歴史]]>[[高等学校歴史総合]]>冷戦終結後の紛争と平和への取り組み
== 冷戦終結後の紛争 ==
== 紛争の解決と国際社会の役割 ==
1992年、湾岸戦争が始まると、日本も世界の平和のために協力するようになりました。そこで、国連平和維持活動等協力法(PKO協力法)を作り、カンボジアに自衛隊を送るようになりました。2001年のアメリカ同時多発テロ事件(9.11事件)からアメリカ軍のお手伝いをするために、海上自衛隊をインド洋へ送りました。また、イラクの復興を手伝うために自衛隊をイラクに送りました。
かつての南アフリカ共和国は、肌の色で人々を分けるような慣習がありました(アパルトヘイト)。もちろん黒人の人たちはこのような差別を受け入れられるはずがなく、政府に声を上げるようになりました。世界各国も「そのような差別は許しません。」と南アフリカ政府に伝えて、様々な内容を制限しました。そんな中、代表的な指導者のネルソン・マンデラが立ち上がり、当時の大統領と力を合わせてアパルトヘイトを無くすために大切な役割を果たしました。1994年、南アフリカで初めて肌の色に関係なく誰でも選挙に参加出来るようになりました。その結果、ネルソン・マンデラが南アフリカ共和国の大統領になりました。
[[カテゴリ:高等学校歴史総合|れいせんしゆうけつこのふんそうとへいわへのとりくみ]]
[[カテゴリ:国際関係学]]
[[カテゴリ:20世紀]]
[[カテゴリ:21世紀]]
h714573sqhsubvpay2amuywrmnjwxm3
C++/CからC++への移行
0
37241
264544
249468
2024-11-30T05:17:20Z
Ef3
694
校閲と推敲と加筆
264544
wikitext
text/x-wiki
{{Nav}}
== はじめに ==
C++は、C言語の強力な基盤の上にオブジェクト指向プログラミングやその他の高レベルな機能を追加した言語です。特にモダンC++(C++11以降の標準)は、コードの安全性、効率性、生産性を向上させる数多くの新機能を提供しています。この章では、C言語のプログラマがC++へ移行する際に役立つ情報を提供し、C++の利点とその新機能を紹介します。
== 基本的な違い ==
C++では、プログラムの構造化や名前の衝突を避けるために、名前空間が導入されています。C++の標準ライブラリは、すべて<code>std</code>という名前空間に含まれています。
:<syntaxhighlight lang=c++ copy>
#include <iostream>
namespace MyNamespace {
void hello() {
std::cout << "Hello from MyNamespace!" << std::endl;
}
}
auto main() -> int {
MyNamespace::hello();
return 0;
}
</syntaxhighlight>
この例では、<code>MyNamespace</code>という名前空間を定義し、その中に<code>hello</code>関数を配置しています。これにより、名前の衝突を避けることができます。
== プログラミングスタイルの変化 ==
C++はオブジェクト指向プログラミング(OOP)をサポートし、データと関数をクラスという単位でまとめることができます。これにより、コードの再利用性や保守性が向上します。
:<syntaxhighlight lang=c++ copy>
class MyClass {
public:
void sayHello() {
std::cout << "Hello, World!" << std::endl;
}
};
auto main() -> int {
MyClass obj;
obj.sayHello();
return 0;
}
</syntaxhighlight>
この例では、<code>MyClass</code>というクラスを定義し、その中に<code>sayHello</code>というメンバ関数を持たせています。<code>main</code>関数では、<code>MyClass</code>のインスタンス<code>obj</code>を作成し、<code>sayHello</code>を呼び出しています。
== メモリ管理とスマートポインタ ==
C言語では、<code>malloc</code>や<code>free</code>を使って手動でメモリ管理を行いますが、C++では<code>new</code>と<code>delete</code>を使って動的メモリを管理します。さらに、C++11以降ではスマートポインタが導入され、メモリ管理が簡単かつ安全になりました。
:<syntaxhighlight lang=c++ copy>
#include <memory>
void useSmartPointer() {
std::unique_ptr<int> p1(new int(5));
std::shared_ptr<int> p2 = std::make_shared<int>(10);
// メモリは自動的に解放される
}
</syntaxhighlight>
<code>std::unique_ptr</code>は単一の所有権を持つポインタで、<code>std::shared_ptr</code>は複数の所有権を共有するポインタです。これにより、メモリリークやダングリングポインタのリスクが減少します。
== 標準ライブラリの利用 ==
C++の標準ライブラリ(STL: Standard Template Library)は、データ構造やアルゴリズムを提供します。これにより、効率的で再利用可能なコードを簡単に書くことができます。
:<syntaxhighlight lang=c++ copy>
#include <vector>
#include <algorithm>
#include <iostream>
auto main() -> int {
std::vector<int> vec = {1, 5, 6, 4, 3};
std::sort(vec.begin(), vec.end());
for (int n : vec) {
std::cout << n << " ";
}
return 0;
}
</syntaxhighlight>
この例では、<code>std::vector</code>を使って整数のリストを作成し、<code>std::sort</code>でソートしています。
== 関数の強化 ==
C++では、関数のオーバーロードやデフォルト引数がサポートされており、同じ名前の関数を異なる引数リストで定義できます。また、C++11以降ではラムダ式が導入され、簡潔に関数を定義できます。
:<syntaxhighlight lang=c++ copy>
#include <iostream>
#include <functional>
void print(int x) {
std::cout << "int: " << x << std::endl;
}
void print(double x) {
std::cout << "double: " << x << std::endl;
}
auto main() -> int {
print(10); // int: 10
print(10.5); // double: 10.5
auto lambda = [](int x) { return x * 2; };
std::cout << lambda(5) << std::endl; // 10
return 0;
}
</syntaxhighlight>
== 型安全と型推論 ==
C++では型安全性が重要視されており、<code>auto</code>キーワードを使ってコンパイラに型を推論させることができます。また、<code>decltype</code>を使って式の型を取得できます。
:<syntaxhighlight lang=c++ copy>
#include <iostream>
auto main() -> int {
auto x = 5; // int
auto y = 5.5; // double
decltype(x) z = 10; // int
std::cout << x << ", " << y << ", " << z << std::endl;
return 0;
}
</syntaxhighlight>
== モダンC++の機能 ==
モダンC++の特徴的な機能として、ムーブセマンティクスと右辺値参照があります。これにより、リソースの効率的な管理が可能となります。
:<syntaxhighlight lang=c++ copy>
#include <iostream>
#include <vector>
void processVector(std::vector<int>&& vec) {
std::cout << "Processing vector of size " << vec.size() << std::endl;
}
auto main() -> int {
std::vector<int> myVec = {1, 2, 3, 4, 5};
processVector(std::move(myVec)); // ムーブセマンティクスを利用
return 0;
}
</syntaxhighlight>
この例では、<code>std::move</code>を使って<code>myVec</code>を右辺値参照として渡し、リソースを効率的に移動しています。
== 例外処理 ==
C++では例外処理を利用して、エラーが発生した際にプログラムの安全な終了を図ることができます。
:<syntaxhighlight lang=c++ copy>
#include <iostream>
#include <stdexcept>
void mayThrow(bool shouldThrow) {
if (shouldThrow) {
throw std::runtime_error("An error occurred!");
}
}
auto main() -> int {
try {
mayThrow(true);
} catch (const std::runtime_error& e) {
std::cerr << "Caught an exception: " << e.what() << std::endl;
}
return 0;
}
</syntaxhighlight>
この例では、<code>mayThrow</code>関数が<code>std::runtime_error</code>を投げ、それを<code>try-catch</code>ブロックで捕捉しています。
== コンパイル時のプログラム ==
テンプレートプログラミングにより、型に依存しない汎用的な関数やクラスを定義できます。また、<code>constexpr</code>を使ってコンパイル時に評価される定数式を定義できます。
:<syntaxhighlight lang=c++ copy>
#include <iostream>
template <typename T>
T add(T a, T b) {
return a + b;
}
constexpr int factorial(int n) {
return (n <= 1) ? 1 : (n * factorial(n - 1));
}
auto main() -> int {
std::cout << "Sum: " << add(5, 3) << std::endl; // Sum: 8
std::cout << "Factorial of 5: " << factorial(5) << std::endl; // Factorial of 5: 120
return 0;
}
</syntaxhighlight>
== C++11以降の新機能 ==
C++11以降、標準ライブラリや言語仕様に多くの新機能が追加されました。これには、ラムダ式、スマートポインタ、非同期処理、コンパイル時の評価などが含まれます。
== C++14以降の機能 ==
C++14では、いくつかの改善が行われました。特に、ラムダ式の機能が強化され、より柔軟に使えるようになりました。また、コンパイル時の最適化が強化され、コードの効率が向上しました。
=== ラムダ式の強化 ===
C++14では、ラムダ式の捕捉リストに<code>decltype</code>を使用することで、引数の型を動的に推論できます。また、ラムダ式で返される型を自動的に推論させることができます。
:<syntaxhighlight lang=c++ copy>
#include <iostream>
auto main() -> int {
int x = 5;
auto lambda = [x](int y) { return x + y; }; // ラムダ式の引数に型を推論
std::cout << lambda(3) << std::endl; // 8
return 0;
}
</syntaxhighlight>
この例では、ラムダ式内でキャプチャされる変数<code>x</code>が、型推論によって自動的に適切に取り扱われます。
=== 型推論の強化 ===
C++14では、型推論が強化され、変数宣言の際に<code>auto</code>をより柔軟に使用できるようになりました。また、C++14からは<code>auto</code>を使って戻り値の型を推論することができ、より簡潔なコードが書けます。
:<syntaxhighlight lang=c++ copy>
#include <iostream>
auto add(int a, int b) {
return a + b; // 戻り値の型は自動的にintとして推論される
}
auto main() -> int {
std::cout << add(5, 3) << std::endl; // 8
return 0;
}
</syntaxhighlight>
C++14では、関数の戻り値の型も<code>auto</code>を使って自動的に推論することができます。
== C++17の新機能 ==
C++17では、さらに多くの新機能が追加され、C++の生産性とパフォーマンスが向上しました。特に、構造化束縛、インライン変数、<code>std::optional</code>、<code>std::variant</code>などの新しいデータ型が注目されます。
=== 構造化束縛 ===
構造化束縛を使用すると、タプルやペアの要素を簡単に変数に展開できます。これにより、複雑なデータ構造を扱う際のコードがシンプルになります。
:<syntaxhighlight lang=c++ copy>
#include <iostream>
#include <tuple>
auto main() -> int {
std::tuple<int, double, std::string> t = {1, 3.14, "Hello"};
auto [x, y, z] = t; // 構造化束縛でタプルの要素を展開
std::cout << x << ", " << y << ", " << z << std::endl; // 1, 3.14, Hello
return 0;
}
</syntaxhighlight>
この例では、<code>std::tuple</code>の要素を<code>[x, y, z]</code>のように簡単に展開しています。
=== インライン変数 ===
C++17では、インライン変数が導入されました。これにより、定数やグローバル変数を<code>constexpr</code>と同様にインラインで定義できるようになりました。
:<syntaxhighlight lang=c++ copy>
#include <iostream>
inline int global_value = 42; // インライン変数
auto main() -> int {
std::cout << global_value << std::endl; // 42
return 0;
}
</syntaxhighlight>
このインライン変数は、複数のソースファイルにわたる定義を簡潔に扱うことができます。
=== std::optional と std::variant ===
C++17では、<code>std::optional</code>と<code>std::variant</code>が追加され、値が存在しない場合や、複数の異なる型を持つ可能性のある値を扱うのが簡単になりました。
:<syntaxhighlight lang=c++ copy>
#include <iostream>
#include <optional>
auto main() -> int {
std::optional<int> val = 10;
if (val) {
std::cout << *val << std::endl; // 10
} else {
std::cout << "No value" << std::endl;
}
return 0;
}
</syntaxhighlight>
<code>std::optional</code>は値が存在するかどうかを簡単にチェックでき、<code>std::variant</code>は複数の型を1つの変数で保持することができます。
== C++20の新機能 ==
C++20では、さらに多くの新機能が追加され、特にコンセプト、コルーチン、<code>std::ranges</code>ライブラリなど、プログラミングの抽象化や生産性を向上させるツールが提供されています。
=== コンセプト ===
コンセプトは、テンプレートパラメータに対する制約を指定する方法を提供します。これにより、コンパイラエラーが発生する前にテンプレートの使用方法が確認でき、コードの安全性が向上します。
:<syntaxhighlight lang=c++ copy>
#include <iostream>
template<typename T>
concept Incrementable = requires(T x) {
{ x++ } -> std::same_as<T&>;
};
template<Incrementable T>
T increment(T x) {
return x++;
}
auto main() -> int {
std::cout << increment(10) << std::endl; // 11
return 0;
}
</syntaxhighlight>
この例では、<code>Incrementable</code>というコンセプトを定義し、テンプレート関数にその制約を適用しています。
=== コルーチン ===
C++20ではコルーチンが導入され、非同期プログラミングがより直感的に書けるようになりました。コルーチンを使うことで、非同期処理を同期的に記述できます。
:<syntaxhighlight lang=c++ copy>
#include <iostream>
#include <coroutine>
std::coroutine_handle<> coroutine_example() {
std::cout << "Start coroutine" << std::endl;
co_return;
}
auto main() -> int {
coroutine_example();
return 0;
}
</syntaxhighlight>
この例では、簡単なコルーチンを使用して非同期処理の流れを示しています。
=== std::ranges ライブラリ ===
C++20では、<code>std::ranges</code>ライブラリが追加され、範囲(range)に対する操作が簡潔に記述できるようになりました。これにより、データ構造やアルゴリズムの操作が直感的になります。
:<syntaxhighlight lang=c++ copy>
#include <iostream>
#include <ranges>
#include <vector>
auto main() -> int {
std::vector<int> vec = {1, 2, 3, 4, 5};
auto result = vec | std::ranges::transform([](int x) { return x * 2; });
for (auto x : result) {
std::cout << x << " ";
}
return 0;
}
</syntaxhighlight>
この例では、<code>std::ranges::transform</code>を使って、ベクター内のすべての要素を2倍にしています。
== まとめ ==
C++は、強力で柔軟なプログラミング言語であり、モダンC++(C++11以降)の新機能を使うことで、コードの効率、可読性、安全性が向上します。新しい機能を積極的に活用することで、より高品質なプログラムを作成できます。
{{Nav}}
{{DEFAULTSORT:CからC++へのいこう}}
[[Category:C++]]
qwu4saflb3s7hgi2t39r06pv7knl82f
アイヌ語
0
37830
264517
262399
2024-11-29T14:52:22Z
BrassSnail
71325
/* ウィキブックスでの編集方針 */
264517
wikitext
text/x-wiki
[[メインページ]] > [[語学]] > '''アイヌ語'''
{{Wikipedia|アイヌ語}}
{{Wiktionary|カテゴリ:アイヌ語}}
'''アイヌ語'''は、北海道を中心に樺太、千島列島、東北地方北部などで話されてきた言語です。日本語と同じく、他の言語との系統関係の分かっていない「孤立した言語」に分類されます。日本語にとっては唯一の陸続きの言語で、古くから互いに言葉を借用し合うなどの大きな関わりがありました。日常の語彙、特に儀礼用語には、日本語とアイヌ語とで共通するものや似ているものが数多くあります。語法の面でも、「〜をやってみる」などの表現は日本語と全く同じような言い方で、隣り合って話されてきた近さを感じます。
アイヌ語の基本的な語順は日本語と同じ主語-目的語-述語です。母音は五種類で、子音も日本語と同じものが多く、上述のように同じ発想の表現もあるため、日本語話者にとっては比較的習得しやすい言語といえます。
その一方、非常に強力な造語力を持ち、多くの要素を繋げて一つの長い単語で文のような役割を表すことができます。
== ウィキブックスでの編集方針 ==
アイヌ語には表記法にバリエーションがある。ウィキブックスは教科書の類に入るので学習時の分かり易さのために、最も多く使われている表記とは異なる表記を採ることがある。
==[[アイヌ語 入門編|入門編]]==
*[[アイヌ語の世界へようこそ]]
*[[アイヌ語 文字入力の準備|文字入力の準備]]
*[[アイヌ語 文字と発音|文字と発音]]
*[[アイヌ語 アクセント|アクセント]]
*[[アイヌ語 音の変化|音の変化]]
*[[アイヌ語 表記の揺れについて|表記の揺れについて]]
*[[アイヌ語 方言について|アイヌ語の方言について]]
==話す、聞く==
=== 会話練習 ===
#[[アイヌ語定型文練習 タンペ へマンタ アン?|これは何?]]
#[[アイヌ語 自己紹介|自己紹介]]
=== 例文集(慣用句、よく使う文) ===
* 挨拶
*
== [[アイヌ語 文法|文法]] ==
*「~は~だ」の形の文
*否定
*動詞の種類
*人称接辞
*位置名詞
==[[アイヌ語 テーマ別重要語彙|テーマ別 重要語彙]]==
*[[アイヌ語 数の数え方|数の数え方]]
*[[アイヌ語 暦|暦]]
*[[アイヌ語 人称接辞の一覧|地域毎の人称接辞一覧]]
*[[アイヌ語 位置名詞の一覧|位置名詞一覧]]
*動植物
== 学習に役立つ本・ウェブサイト<!-- 辞書欄を除いては、情報が溢れないようにし、入門者にも使いやすく信頼の置けるものを重点的に載せてください。 -->==
=== 総合 ===
* [https://www.ff-ainu.or.jp/web/potal_site/index.html アイヌ民族文化財団 アイヌ語ポータルサイト]
* [https://www.bunka.go.jp/seisaku/kokugo_nihongo/kokugo_shisaku/kikigengo/ainu/index.html 文化庁 アイヌ語に関するリンク集]
=== 音声を聞く ===
* [https://ainugo.nam.go.jp/search/media?typeCont=on&typeAudio=on&typeVideo=on 国立アイヌ民族博物館アイヌ語アーカイブ 資料検索]
* [https://ainugo.hm.pref.hokkaido.lg.jp/generalSearchResult.aspx ほっかいどう アイヌ語アーカイブ|北海道博物館]
* [https://www.youtube.com/@user-jr8bk2iy1n YouTube 公益財団法人アイヌ民族文化財団のチャンネル]
=== 独習書 ===
* 『ニューエクスプレスプラス アイヌ語』(ISBN 978-4-560-08868-5)中川裕(2021)
::「会話から文法を一冊で学べる入門書に」という言葉の通り、初めてアイヌ語を学ぶ人にはおすすめの一冊。CDやアプリで音声を聞く事もできる。
=== 文法を学ぶ ===
* 『言語学大辞典』第一巻(ISBN 978-4-385-15213-4)亀井孝 他編 所収「アイヌ語」田村すゞ子(1997)
:: 分冊版の『言語学大辞典セレクション 日本列島の言語』(ISBN 4-385-15207-1)にも同じ内容が書かれている。
=== 辞書 ===
*『アイヌ語千歳方言辞典』中川裕(1995)
*『アイヌ語沙流方言辞典』田村すゞ子(1996)
*『萱野茂のアイヌ語辞典』萱野茂(1996)
*[https://ainugo.nam.go.jp/ 国立アイヌ民族博物館アイヌ語アーカイブ]
*[http://tommy1949.world.coocan.jp/aynudictionary.htm アイヌ語電子辞書]
::辞書以外の資料からも検索できる。辞書は、田村すず子『アイヌ語沙流方言辞典』(1996)、萱野茂著『萱野茂のアイヌ語辞典』(1996)、知里真志保『分類アイヌ語辞典 植物編・動物編』(1953)の3冊を基にしている。
=== 物語 ===
*『アイヌ神謡集』知里幸惠
=== その他(アイヌ語に留まらない、様々な文化、出来事について) ===
*ゴールデンカムイ
**野田サトル氏の漫画。北海道や樺太を舞台に、日露戦争で「不死身の杉元」と恐れられた杉元佐一、アイヌの少女アシㇼパ、網走監獄の囚人達や陸軍の軍人などが様々な思惑で金塊を探すというストーリー。創作ではあるが、登場するアイヌや他の少数民族の文化・風俗はかなり精密に描かれている。
[[カテゴリ:語学]]
[[カテゴリ:アイヌ語|*]]
0wbu9mhhilr4vrb2og5fkbsdqz0y5ll
デザインパターン
0
38951
264520
241206
2024-11-29T16:18:44Z
Ef3
694
/* 振る舞い(Behavioral)デザインパターン */ 全体的な文章の推敲:より明確で洗練された表現に修正しました。 各デザインパターンにRubyでの具体的なコード例を追加しました。 各パターンの説明をより詳細かつ実践的なものに更新しました。 コード例は実際に動作するシンプルな実装を心がけました。 パターンの本質的な目的と利点を明確にしました。
264520
wikitext
text/x-wiki
コンピューターサイエンスにおけるデザインパターンは、ソフトウェア設計において再利用可能な解決策の一般的なモデルやテンプレートです。これらのパターンは、共通の課題や問題に対処するために繰り返し使われ、設計の柔軟性、拡張性、保守性を向上させます。デザインパターンは、ソフトウェアエンジニアリングのコミュニティで広く受け入れられており、GoF(Gang of Four)による "Design Patterns: Elements Of Reusable Object-Oriented Software"(邦題『オブジェクト指向における再利用のためのデザインパターン』)などの文献で有名です。
デザインパターンの主な特徴は以下の通りです:
; 再利用性: デザインパターンは、共通の設計上の問題に対処するための汎用的な解決策を提供するため、再利用が容易です。同じまたは類似の課題に対して何度も設計を行う必要がなくなります。
; 柔軟性: デザインパターンは柔軟性をもたらし、変更が必要な場合にも容易に対応できるような設計を促進します。新しい要件や機能の追加があっても、デザインパターンを利用することで変更の影響を最小限に抑えられます。
; 共通の語彙: デザインパターンは、ソフトウェア開発者やエンジニアの間で共通の語彙を提供します。これにより、チーム内でのコミュニケーションが向上し、設計の理解が容易になります。
; ソフトウェアアーキテクチャ: デザインパターンはソフトウェアアーキテクチャを構築するための基本的な構成要素として使われます。これにより、ソフトウェアの構造が理解しやすく、拡張が容易になります。
一部の有名なデザインパターンには、Singleton(単一のインスタンスを保持するパターン)、Factory Method(ファクトリークラスがオブジェクトの生成を担当するパターン)、Observer(オブジェクトの状態変化を監視するパターン)などがあります。これらのパターンはソフトウェア設計の中で広く利用され、開発者が共通の問題に対してより効果的にアプローチできるようになります。
== デザインパターンの分類と概要 ==
デザインパターンは、ゴフ(Gang of Four)によって提案された23のパターンを、以下の3つの大きなカテゴリに分類できます。それぞれのカテゴリには、ソフトウェア開発における特定の課題に対処するためのパターンが含まれています。
=== 生成(Creational)デザインパターン ===
生成デザインパターンは、オブジェクトの生成に焦点を当て、オブジェクトの生成と構成に関する柔軟で再利用可能な手法を提供します。これらのパターンは、インスタンスの生成方法やクラスの初期化に関する課題に対処し、アプリケーションの柔軟性と保守性を向上させます。
==== Singleton(シングルトン)パターン ====
シングルトンパターンは、アプリケーション内で唯一のインスタンスを持つように設計されたデザインパターンです。このパターンは、クラスが単一のインスタンスしか持たないようにし、その唯一のインスタンスにアクセスするための手法を提供します。以下に、シングルトンパターンの基本的な説明とRubyでのコード例を示します。
; シングルトンパターンの特徴
# 単一のインスタンス:
#* シングルトンクラスは一つのインスタンスしか持ちません。
# グローバルアクセスポイント:
#* シングルトンインスタンスにアクセスするためのグローバルなアクセスポイントが提供されます。
# 共有リソース管理:
#* シングルトンパターンは、共有のリソースや設定にアクセスする場合に有用です。例えば、データベース接続やログ管理など。
; Rubyでのシングルトンパターンの実装(1)
:<syntaxhighlight lang=ruby>
class SingletonClass
# クラス変数で唯一のインスタンスを保持
@@instance = nil
# newメソッドをprivateにすることで、外部から直接インスタンス化できなくなる
private_class_method :new
# インスタンスにアクセスするためのメソッド
def self.instance() @@instance ||= new end
# その他のシングルトンクラスのメソッドやプロパティ
def some_method() puts "Singleton instance method called" end
end
# シングルトンクラスのインスタンスを取得
singleton_instance = SingletonClass.instance
singleton_instance.some_method
</syntaxhighlight>
この例では、<code>SingletonClass</code>クラスがシングルトンパターンを実現しています。<code>instance</code>メソッドを通じて唯一のインスタンスにアクセスできます。<code>new</code>メソッドは<code>private_class_method</code>でprivateになっており、外部から直接インスタンス化ができないようになっています。
Rubyには、シングルトンパターンをクラスで実践するために <code>Singleton</code> Mix-in が用意されています。
; Rubyでのシングルトンパターンの実装(2)
:<syntaxhighlight lang=ruby>
require 'singleton'
class SingletonClass
include Singleton
# その他のシングルトンクラスのメソッドやプロパティ
def some_method() puts "Singleton instance method called" end
end
# シングルトンクラスのインスタンスを取得
singleton_instance = SingletonClass.instance
singleton_instance.some_method
</syntaxhighlight>
この例では、<code>SingletonClass</code>クラスが<code>Singleton</code>モジュールを<code>include</code>しています。これにより、<code>SingletonClass</code>のインスタンスはシングルトンとして扱われ、<code>instance</code>メソッドで唯一のインスタンスにアクセスできます。
<code>singleton</code>モジュールを使用することで、シングルトンパターンの実装が簡潔になり、また必要なメソッドやプロパティを追加できます。シングルトンパターンは、共有リソースへのアクセスや設定管理などのシナリオで役立ちます。
このように実装されたシングルトンパターンでは、複数のインスタンスが作成されることを防ぎ、唯一の共有リソースへのアクセスを確実に制御できます。
==== Factory Method(ファクトリーメソッド)パターン ====
Factory Methodパターンは、オブジェクトの生成をサブクラスに委譲し、具体的な生成プロセスをサブクラスで実装する手法です。これにより、生成されるオブジェクトの型をサブクラスによって動的に変更できます。Factory Methodは、生成するオブジェクトの種類に依存せず、柔軟性を提供するために使用されます。
; Factory Method パターンの特徴
# 抽象クラスまたはインターフェース:
#* Factory Methodパターンでは、オブジェクトの生成を抽象クラスまたはインターフェースで定義します。
# サブクラスでの生成プロセス実装:
#* 具体的な生成プロセスはサブクラスに委譲され、サブクラスでそれぞれの生成方法が実装されます。
# 動的な型変更:
#* Factory Methodによって生成されるオブジェクトの型は、実行時にサブクラスの選択によって動的に変更できます。
; RubyでのFactory Method パターンの実装
: 以下は、Factory Methodパターンの簡単なコード例です。
:<syntaxhighlight lang=ruby>
# Product(生成されるオブジェクト)の抽象クラス
class Product
def operation() raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
end
# ConcreteProduct1(具体的な生成物1)
class ConcreteProduct1 < Product
def operation() "Operation from ConcreteProduct1" end
end
# ConcreteProduct2(具体的な生成物2)
class ConcreteProduct2 < Product
def operation() "Operation from ConcreteProduct2" end
end
# Creator(生成するオブジェクトの抽象クラス)
class Creator
def factory_method() raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
def some_operation()
product = factory_method
"Creator: #{product.operation}"
end
end
# ConcreteCreator1(具体的な生成者1)
class ConcreteCreator1 < Creator
def factory_method() ConcreteProduct1.new end
end
# ConcreteCreator2(具体的な生成者2)
class ConcreteCreator2 < Creator
def factory_method() ConcreteProduct2.new end
end
# クライアントコード
def client_code(creator) creator.some_operation end
# 具体的な生成者1を利用する場合
creator1 = ConcreteCreator1.new
puts client_code(creator1) # Output: "Creator: Operation from ConcreteProduct1"
# 具体的な生成者2を利用する場合
creator2 = ConcreteCreator2.new
puts client_code(creator2) # Output: "Creator: Operation from ConcreteProduct2"
</syntaxhighlight>
この例では、<code>Product</code>が生成されるオブジェクトを表す抽象クラスであり、<code>ConcreteProduct1</code>および<code>ConcreteProduct2</code>が具体的な生成物を表します。<code>Creator</code>が生成するオブジェクトの抽象クラスであり、<code>ConcreteCreator1</code>および<code>ConcreteCreator2</code>が具体的な生成者を表します。クライアントコードは、具体的な生成者を選択して生成物を得ることができます。
Factory Methodパターンを使用することで、クライアントコードは生成物の種類に依存せず、柔軟に生成プロセスを変更できるメリットがあります。
==== Abstract Factory(抽象ファクトリー)パターン ====
Abstract Factoryパターンは、関連する一連のオブジェクトを生成するためのインターフェースを提供し、一貫性を持たせます。異なるファクトリーを利用することで、異なるオブジェクトのファミリーを生成できます。このパターンは、オブジェクトが互いに関連しており、一緒に利用される場合に特に有用です。
; Abstract Factory パターンの要素
# AbstractFactory(抽象ファクトリー):
#* オブジェクトの生成に関するインターフェースを定義します。関連する一連のオブジェクトを生成するメソッドを提供します。
# ConcreteFactory(具体的なファクトリー):
#* <code>AbstractFactory</code>を実装し、具体的なオブジェクトの生成を行います。通常、同じファミリーのオブジェクトを生成します。
# AbstractProduct(抽象製品):
#* 生成されるオブジェクトの抽象クラスまたはインターフェースを定義します。
# ConcreteProduct(具体的な製品):
#* <code>AbstractProduct</code>を実装し、具体的なオブジェクトのクラスを提供します。
# Client(クライアント):
#* Abstract Factoryを利用してオブジェクトを生成します。クライアントは具体的なファクトリーを意識せずにオブジェクトを取得できます。
; Rubyでの Abstract Factory パターンの実装
: 以下は、Abstract Factoryパターンの簡単なコード例です。
:<syntaxhighlight lang=ruby>
# AbstractProduct
class Button
def click() raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
end
# ConcreteProduct1
class WindowsButton < Button
def click() "Windows button clicked" end
end
# ConcreteProduct2
class MacOSButton < Button
def click() "MacOS button clicked" end
end
# AbstractProduct
class Checkbox
def check() raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
end
# ConcreteProduct1
class WindowsCheckbox < Checkbox
def check() "Windows checkbox checked" end
end
# ConcreteProduct2
class MacOSCheckbox < Checkbox
def check() "MacOS checkbox checked" end
end
# AbstractFactory
class GUIFactory
def create_button() raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
def create_checkbox() raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
end
# ConcreteFactory1
class WindowsFactory < GUIFactory
def create_button() WindowsButton.new end
def create_checkbox() WindowsCheckbox.new end
end
# ConcreteFactory2
class MacOSFactory < GUIFactory
def create_button() MacOSButton.new end
def create_checkbox() MacOSCheckbox.new end
end
# Client
class Application
def initialize(factory)
@button = factory.create_button
@checkbox = factory.create_checkbox
end
def run()
puts @button.click
puts @checkbox.check
end
end
# クライアントコード
windows_app = Application.new(WindowsFactory.new)
macos_app = Application.new(MacOSFactory.new)
windows_app.run
# Output:
# "Windows button clicked"
# "Windows checkbox checked"
macos_app.run
# Output:
# "MacOS button clicked"
# "MacOS checkbox checked"
</syntaxhighlight>
この例では、WindowsおよびMacOS用のボタンとチェックボックスを生成する<code>GUIFactory</code>が抽象ファクトリーとなります。具体的な製品はそれぞれ<code>WindowsButton</code>、<code>WindowsCheckbox</code>、<code>MacOSButton</code>、<code>MacOSCheckbox</code>です。<code>Application</code>クラスが抽象ファクトリーを通じてオブジェクトを生成し、クライアントコードが異なるプラットフォーム用のオブジェクトを利用できます。
==== Builder(ビルダー)パターン ====
Builderパターンは、複雑なオブジェクトの構築プロセスを抽象化し、同じ構築プロセスを使用して異なる表現のオブジェクトを作成できるようにします。これにより、柔軟で再利用可能な構築手法を提供します。Builderパターンは、複雑なオブジェクトを一貫して構築する必要があり、同じ構築プロセスで異なるオブジェクトを生成する必要がある場合に有用です。
; Builder パターンの要素
# Product(製品):
#* ビルダーで構築されるオブジェクトの表現を定義します。
# Builder(ビルダー):
#* 複雑なオブジェクトを構築するための抽象インターフェースを提供します。具体的なビルダーがこのインターフェースを実装します。
# ConcreteBuilder(具体的なビルダー):
#* Builderを実装し、製品の各部分の構築を担当します。ビルドされた製品を取得するメソッドも提供します。
# Director(ディレクター):
#* クライアントからの指示に基づいてビルダーを利用してオブジェクトを構築します。ビルダーの組み合わせ方を知っています。
; Rubyでの Builder パターンの実装
: 以下は、Builderパターンの簡単なコード例です。
:<syntaxhighlight lang=ruby>
# Product
class Computer
attr_accessor :cpu, :memory, :storage
def to_s() "Computer with CPU: #{@cpu}, Memory: #{@memory}, Storage: #{@storage}" end
end
# Builder
class ComputerBuilder
def build_cpu() raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
def build_memory() raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
def build_storage() raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
def computer() raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
end
# ConcreteBuilder
class HighPerformanceComputerBuilder < ComputerBuilder
def initialize() @computer = Computer.new end
def build_cpu() @computer.cpu = "High Performance CPU" end
def build_memory() @computer.memory = "16GB RAM" end
def build_storage() @computer.storage = "1TB SSD" end
def computer() @computer end
end
# Director
class ComputerEngineer
def initialize(builder) @builder = builder end
def assemble_computer()
@builder.build_cpu
@builder.build_memory
@builder.build_storage
end
def get_computer() @builder.computer end
end
# クライアントコード
builder = HighPerformanceComputerBuilder.new
engineer = ComputerEngineer.new(builder)
engineer.assemble_computer
high_performance_computer = engineer.get_computer
puts high_performance_computer.to_s
# Output: "Computer with CPU: High Performance CPU, Memory: 16GB RAM, Storage: 1TB SSD"
</syntaxhighlight>
この例では、<code>Computer</code>がビルダーパターンで構築される製品を表しています。<code>ComputerBuilder</code>がビルダーの抽象クラスであり、<code>HighPerformanceComputerBuilder</code>が具体的なビルダーです。<code>ComputerEngineer</code>がディレクターで、ビルダーを利用して製品を組み立てます。クライアントコードはディレクターを通じてビルダーを利用して製品を構築し、最終的な製品を取得します。
このように、Builderパターンを使用することで、複雑なオブジェクトの構築プロセスをカプセル化し、柔軟かつ再利用可能な方法で異なる表現のオブジェクトを生成できます。
==== Prototype(プロトタイプ)パターン ====
Prototypeパターンは、オブジェクトのコピーを作成する手法を提供し、新しいオブジェクトを既存のオブジェクトを基にして生成できるようにします。これにより、オブジェクトの生成コストを削減できます。プロトタイプパターンは、同じようなオブジェクトが複数必要であり、それぞれのオブジェクトが少しだけ異なる場合に有用です。
; Prototype パターンの要素
# Prototype(プロトタイプ):
#* オブジェクトのコピーを作成するためのインターフェースを提供します。
# ConcretePrototype(具体的なプロトタイプ):
#* <code>Prototype</code>を実装し、オブジェクトのコピーを作成する具体的な実装を提供します。
# Client(クライアント):
#* プロトタイプを使用して新しいオブジェクトのコピーを作成します。
; Rubyでの Prototype パターンの実装
:以下は、Prototypeパターンの簡単なコード例です。
:<syntaxhighlight lang=ruby>
# Prototype
class Cloneable
def clone() raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
end
# ConcretePrototype
class ConcreteCloneable < Cloneable
attr_accessor :attribute
def initialize(attribute) @attribute = attribute end
def clone() self.class.new(@attribute) end
end
# Client
class ObjectCloner
def initialize(prototype) @prototype = prototype end
def clone_object() @prototype.clone end
end
# クライアントコード
original_object = ConcreteCloneable.new("Original Attribute")
cloner = ObjectCloner.new(original_object)
cloned_object = cloner.clone_object
puts "Original Object Attribute: #{original_object.attribute}"
puts "Cloned Object Attribute: #{cloned_object.attribute}"
# Output:
# Original Object Attribute: Original Attribute
# Cloned Object Attribute: Original Attribute
</syntaxhighlight>
この例では、<code>Cloneable</code>がプロトタイプの抽象クラスを表し、<code>ConcreteCloneable</code>が具体的なプロトタイプを表しています。<code>ObjectCloner</code>がクライアントで、指定されたプロトタイプを使用して新しいオブジェクトのコピーを作成します。クライアントコードは、元のオブジェクトとクローンされたオブジェクトの属性を出力しています。
このようにプロトタイプパターンを使用することで、オブジェクトの生成コストを低減し、柔軟性を向上させることができます。
:これらの生成デザインパターンは、オブジェクトの生成と初期化に関連する様々な課題に対処し、柔軟性や再利用性を向上させるために広く使用されています。それぞれのパターンは異なる状況や要件に対応し、効果的なソフトウェア設計を支援します。
=== 構造(Structural)デザインパターン ===
構造デザインパターンは、ソフトウェア設計においてクラスやオブジェクトの組み合わせを利用して大きな構造を形成する方法を提供します。これらのパターンは、ソフトウェアのコンポーネントを効果的に組み合わせ、機能や構造を拡張しやすくするための手法を提供します。
==== Adapter(アダプター)パターン ====
アダプターパターンは、互換性のないインターフェースを持つクラス同士を連携させるための手法を提供します。これにより、既存のクラスを変更せずに連携できるようになります。アダプターは、既存のクラスと新しいクラスの仲介を行います。
;クラスベースのアダプター
:クラスベースのアダプターパターンでは、既存のクラスを継承して新しいクラスを作成します。
:<syntaxhighlight lang=ruby>
# 既存のクラス
class LegacySystem
def specific_request() "Legacy System Request" end
end
# アダプタークラス
class Adapter < LegacySystem
def adapted_request() "Adapter: #{specific_request}" end
end
# クライアントコード
adapter = Adapter.new
puts adapter.adapted_request
# Output: "Adapter: Legacy System Request"
</syntaxhighlight>
;オブジェクトベースのアダプター
:オブジェクトベースのアダプターパターンでは、既存のクラスのオブジェクトを新しいクラスの内部で使用します。
:<syntaxhighlight lang=ruby>
# 既存のクラス
class LegacySystem
def specific_request() "Legacy System Request" end
end
# アダプタークラス
class Adapter
def initialize(legacy_system) @legacy_system = legacy_system end
def adapted_request() "Adapter: #{@legacy_system.specific_request}" end
end
# クライアントコード
legacy_system = LegacySystem.new
adapter = Adapter.new(legacy_system)
puts adapter.adapted_request
# Output: "Adapter: Legacy System Request"
</syntaxhighlight>
アダプターパターンは、既存のコードとの互換性を維持しながら新しいコードを導入する際に役立ちます。新しいクラスを既存のクラスと同じように振る舞わせることができ、クライアントコードは変更せずに利用できるようになります。
==== Bridge(ブリッジ)パターン ====
ブリッジパターンは、抽象化と実装を別々に拡張できるようにし、これらの間に橋渡しを行う手法を提供します。これにより、異なる抽象化と実装を組み合わせて新しい機能を追加できます。ブリッジパターンは、抽象クラスと実装クラスを分離して、それらが独立して拡張できるようにします。
;クラスベースのブリッジ
:クラスベースのブリッジパターンでは、抽象クラスと実装クラスがそれぞれ独立して拡張されます。
:<syntaxhighlight lang=ruby>
# 実装層
class Implementation
def operation_implementation() raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
end
# 具体的な実装クラス
class ConcreteImplementationA < Implementation
def operation_implementation() "Concrete Implementation A" end
end
class ConcreteImplementationB < Implementation
def operation_implementation() "Concrete Implementation B" end
end
# 抽象化
class Abstraction
def initialize(implementation) @implementation = implementation end
def operation() "Abstraction: #{implementation_operation}" end
private
def implementation_operation() @implementation.operation_implementation end
end
# クライアントコード
implementation_a = ConcreteImplementationA.new
abstraction_a = Abstraction.new(implementation_a)
puts abstraction_a.operation
# Output: "Abstraction: Concrete Implementation A"
implementation_b = ConcreteImplementationB.new
abstraction_b = Abstraction.new(implementation_b)
puts abstraction_b.operation
# Output: "Abstraction: Concrete Implementation B"
</syntaxhighlight>
;オブジェクトベースのブリッジ
:オブジェクトベースのブリッジパターンでは、抽象クラスと実装クラスをそれぞれオブジェクトとして組み合わせます。
:<syntaxhighlight lang=ruby>
# 実装層
class Implementation
def operation_implementation() raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
end
# 具体的な実装クラス
class ConcreteImplementationA < Implementation
def operation_implementation() "Concrete Implementation A" end
end
class ConcreteImplementationB < Implementation
def operation_implementation() "Concrete Implementation B" end
end
# 抽象化
class Abstraction
def initialize(implementation) @implementation = implementation end
def operation() "Abstraction: #{@implementation.operation_implementation}" end
end
# クライアントコード
implementation_a = ConcreteImplementationA.new
abstraction_a = Abstraction.new(implementation_a)
puts abstraction_a.operation
# Output: "Abstraction: Concrete Implementation A"
implementation_b = ConcreteImplementationB.new
abstraction_b = Abstraction.new(implementation_b)
puts abstraction_b.operation
# Output: "Abstraction: Concrete Implementation B"
</syntaxhighlight>
ブリッジパターンは、抽象化と実装の組み合わせが複雑になる場合や、将来的な変更に備えて柔軟性を持たせる場合に有用です。
==== Composite(コンポジット)パターン ====
コンポジットパターンは、オブジェクトとオブジェクトの構造を同一視し、個々のオブジェクトとその構造を再帰的に扱う手法を提供します。これにより、単一のオブジェクトと複数のオブジェクトの同一視が可能になります。このパターンは、個々のオブジェクトとオブジェクトのコンポジション(構造)を同じように扱い、クライアントが統一的に操作できるようにします。
; 特徴
# 同一視: コンポジットパターンでは、個々のオブジェクト(葉ノード)とオブジェクトの構造(複合ノード)を同じく扱います。これにより、単一のオブジェクトと複数のオブジェクトを同一視できます。
# 再帰的構造: 複合ノードは他の葉ノードや複合ノードを子に持つことができ、これが再帰的な構造を作ります。これにより、階層的で複雑な構造を表現できます。
# クライアントの簡略化: クライアントコードは、単一のオブジェクトとコンポジションを同じように扱うことができ、構造の詳細を気にせずに統一的な操作が可能です。
; 用途
# グラフィカルな構造: グラフィカルなツリーや図形の構造において、個々の要素とグループ(コンポジション)を同一視するのに適しています。
# ファイルシステム: ファイルやディレクトリといった構造を再帰的に扱う場合に利用されます。各要素はファイルかディレクトリかを同じように扱います。
# メニューシステム: GUIアプリケーションにおいて、メニュー項目とサブメニューを同様の要素として扱うのに適しています。
; コード例
:<syntaxhighlight lang=ruby>
# コンポーネント
class Component
def operation
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
end
# 葉ノード
class Leaf < Component
def operation() "Leaf" end
end
# 複合ノード
class Composite < Component
def initialize() @children = [] end
def add(child) @children << child end
def remove(child) @children.delete(child) end
def operation()
results = @children.map(&:operation).join(', ')
"Composite: [#{results}]"
end
end
# クライアントコード
leaf1 = Leaf.new
leaf2 = Leaf.new
composite = Composite.new
composite.add(leaf1)
composite.add(leaf2)
puts leaf1.operation
# Output: "Leaf"
puts composite.operation
# Output: "Composite: [Leaf, Leaf]"
</syntaxhighlight>
この例では、<code>Leaf</code>は葉ノードを、<code>Composite</code>は複合ノードを表しています。クライアントコードは、葉ノードと複合ノードを同様に扱っています。
==== Decorator(デコレーター)パターン ====
デコレーターパターンは、オブジェクトに新しい機能を動的に追加できるようにする手法を提供します。これにより、クラスを拡張する際に継承を使わずに機能を追加できます。デコレーターパターンは、既存のクラスを変更せずに新しい機能を追加する柔軟な手段を提供します。
; 特徴
# 柔軟な機能追加: デコレーターパターンは、既存のクラスを拡張せずに新しい機能を追加できます。これにより、クラスの変更が最小限に抑えられます。
# 再帰的なスタッキング: 複数のデコレーターを組み合わせて使用することができます。これにより、機能を再帰的にスタックすることが可能です。
# オープンクローズドの原則: 新しい機能は既存のコードを変更せずに追加できるため、オープンクローズドの原則をサポートします。
; 用途
# GUIコンポーネント: ウィンドウやボタンなどのGUIコンポーネントにおいて、外観や動作を動的に変更したい場合に利用されます。
# ファイルハンドリング: ファイルやストリームの処理において、データを暗号化する、圧縮するなどの機能を追加するのに適しています。
# ログ出力: ログ出力において、ログのフォーマットやレベルを動的に変更する際に利用されます。
; コード例
:<syntaxhighlight lang=ruby>
# コンポーネント
class Component
def operation() "Component" end
end
# デコレーター基底クラス
class Decorator < Component
def initialize(component) @component = component end
def operation() @component.operation end
end
# 具体的なデコレータークラス
class ConcreteDecoratorA < Decorator
def operation() "ConcreteDecoratorA: [#{super}]" end
end
class ConcreteDecoratorB < Decorator
def operation() "ConcreteDecoratorB: [#{super}]" end
end
# クライアントコード
component = Component.new
decorator_a = ConcreteDecoratorA.new(component)
decorator_b = ConcreteDecoratorB.new(decorator_a)
puts decorator_b.operation
# Output: "ConcreteDecoratorB: [ConcreteDecoratorA: [Component]]"
</syntaxhighlight>
この例では、<code>Component</code>が基本のコンポーネントを表し、<code>Decorator</code>がデコレーターの基底クラスを、<code>ConcreteDecoratorA</code>と<code>ConcreteDecoratorB</code>が具体的なデコレータークラスを表しています。クライアントコードは、これらのデコレーターを組み合わせて新しい機能を追加しています。
==== Facade(ファサード)パターン ====
ファサードパターンは、ソフトウェアデザインにおいて、サブシステムの一連のインターフェースを提供し、クライアントがサブシステムをより簡単に利用できるようにする手法を指します。このデザインパターンは、クライアントとサブシステムとの間に中間層を導入し、クライアントにとって使いやすい方法でサブシステムを操作できるようにします。これにより、クライアントはサブシステムの詳細に関与することなく、シンプルで一貫性のあるAPIを使用して機能を利用できます。
; 特徴:
:; 簡単なインターフェース提供: ファサードパターンは、サブシステム全体にわたる単一の簡潔なインターフェースを提供します。クライアントはこのファサードを通じてサブシステムを利用し、複雑な構造や相互作用を気にする必要がありません。
:; サブシステムの複雑性を隠蔽: ファサードはサブシステムの内部構造や複雑な操作をクライアントから隠蔽します。これにより、クライアントは必要な機能をシンプルなインターフェースを介して利用でき、サブシステムの詳細に気を取られることなくコードを記述できます。
:; カプセル化: サブシステムの各コンポーネントはファサードによってカプセル化され、外部からの直接アクセスを防ぎます。これにより、サブシステム内の変更が影響を及ぼす範囲が狭まります。
; 利点:
:; シンプルな利用: ファサードを使用することで、クライアントは複雑なサブシステムを理解せずにシンプルなインターフェースを利用できます。これにより、プログラムの理解や保守が容易になります。
:; 柔軟性と拡張性: サブシステムの変更や新しい機能の追加があっても、ファサードを変更するだけで済みます。これにより、システム全体の柔軟性と拡張性が向上します。
:; クライアントとサブシステムの分離: クライアントはサブシステムの詳細について知る必要がなくなります。ファサードによって、クライアントとサブシステムが疎結合になり、それぞれの変更が影響を及ぼしにくくなります。
音声処理システムのファサードパターンの例を示します。
:<syntaxhighlight lang=ruby>
# サブシステムの一部
class AudioPlayer
def play(file) puts "Playing audio file: #{file}" end
end
class AudioConverter
def convert(file, format) puts "Converting audio file #{file} to #{format}" end
end
class AudioMixer
def mix(files) puts "Mixing audio files: #{files.join(', ')}" end
end
# ファサード
class AudioFacade
def initialize()
@player = AudioPlayer.new
@converter = AudioConverter.new
@mixer = AudioMixer.new
end
def play_audio(file) @player.play(file) end
def convert_audio(file, format) @converter.convert(file, format) end
def mix_audio(files) @mixer.mix(files) end
end
# クライアント
if __FILE__ == $0
audio_facade = AudioFacade.new
# クライアントはファサードを通してサブシステムを利用
audio_facade.play_audio("song.mp3")
audio_facade.convert_audio("song.mp3", "wav")
audio_facade.mix_audio(["song1.wav", "song2.wav"])
end
</syntaxhighlight>
この例では、<code>AudioFacade</code>がファサードとして機能し、クライアントはこのファサードを介して音声処理システムを利用します。クライアントはサブシステムの詳細について心配する必要がなく、ファサードが提供する単純で使いやすいインターフェースを通じて操作を行います。
==== Flyweight(フライウェイト)パターン ====
フライウェイトパターンは、メモリ使用量を最適化するために多くの類似したオブジェクトを共有するデザインパターンです。このパターンでは、共有される部分とインスタンス固有の部分を分離し、共有される部分を再利用することで効率的なリソース利用を実現します。特に大規模なオブジェクトの集合を扱う場合に威力を発揮します。
; 特徴:
:; 共有: フライウェイトは、同じ状態を持つ複数のオブジェクトが共有されることによってメモリ使用量を最適化します。これにより、大量のオブジェクトが同じデータを保持する場合に効果を発揮します。
:; 状態の内外部化: フライウェイトは、内部状態(共有可能な部分)と外部状態(インスタンス固有の部分)を分離します。共有可能な部分は共有され、インスタンス固有の部分はインスタンスごとに持たれます。
:; イミュータブル: フライウェイトの内部状態は通常、イミュータブルであることが好まれます。これにより、共有状態が変更されることなく安全に共有できます。
; 利点:
:; メモリ効率: 類似のオブジェクトが共有されることで、メモリ使用量が減少し、効率的なリソース利用が可能になります。
:; インスタンスの生成コスト低減: インスタンスの生成がコストが高い場合、同じデータを持つオブジェクトを共有することで生成コストを低減できます。
:; 変更容易性: 共有状態が変更される場合、それが全ての参照先に影響を与えるため、変更が容易になります。
; フライウェイトパターンのコード例:
:<syntaxhighlight lang=ruby>
# フライウェイト
class SharedData
attr_reader :data
def initialize(data) @data = data end
end
# フライウェイトファクトリ
class FlyweightFactory
def initialize() @flyweights = {} end
def get_flyweight(key) @flyweights[key] ||= SharedData.new(key) end
def flyweights_count() @flyweights.length end
end
# クライアント
class Client
def initialize(factory)
@factory = factory
@flyweights = []
end
def get_or_create_flyweight(key)
flyweight = @factory.get_flyweight(key)
@flyweights.push(flyweight)
end
def report
puts "Number of unique flyweights: #{flyweights_count}"
end
private
def flyweights_count()
@flyweights.map(&:object_id).uniq.length
end
end
# クライアントの使用例
if __FILE__ == $0
factory = FlyweightFactory.new
client = Client.new(factory)
client.get_or_create_flyweight("A")
client.get_or_create_flyweight("B")
client.get_or_create_flyweight("A")
client.report
# 出力: Number of unique flyweights: 2
end
</syntaxhighlight>
この例では、<code>FlyweightFactory</code>が<code>SharedData</code>のインスタンスを共有し、<code>Client</code>がそれを利用しています。<code>Client</code>は同じデータを持つ<code>SharedData</code>を共有することで、効率的にメモリを利用しています。
==== Proxy(プロキシ)パターン ====
プロキシパターンは、別のオブジェクトにアクセスするためのプレースホルダーを提供し、そのアクセスを制御するデザインパターンです。主にアクセス制御、キャッシュ、遠隔アクセス、ログなどの機能を追加したり、本物のオブジェクトを生成する遅延初期化を行ったりする場合に使用されます。
; 特徴:
:; 制御機能の追加: プロキシは本物のオブジェクトへのアクセスを制御するため、特定の機能を追加することができます。例えば、アクセス制御、キャッシュ、遠隔アクセス、ログなど。
:; 遅延初期化: プロキシは本物のオブジェクトの生成を遅延させることができます。本物のオブジェクトが本当に必要になるまで生成を遅らせ、リソースの効率的な利用が可能です。
:; 単純なインターフェース: プロキシは本物のオブジェクトと同じインターフェースを提供します。クライアントはプロキシと本物のオブジェクトを同様に扱うことができます。
; 利点:
:; アクセス制御: プロキシは本物のオブジェクトへのアクセスを制御できるため、セキュリティや権限の管理が容易になります。
:; 遅延初期化: 本物のオブジェクトの生成を遅延させることで、リソースを効率的に利用できます。
:; 透過性: プロキシは本物のオブジェクトと同じインターフェースを提供するため、クライアントはプロキシと本物のオブジェクトを同様に扱うことができます。
; プロキシパターンのコード例:
:<syntaxhighlight lang=ruby>
# 本物のオブジェクト
class RealSubject
def request() puts "RealSubject handles the request" end
end
# プロキシ
class Proxy
def initialize(real_subject) @real_subject = real_subject end
def request
check_access
@real_subject.request
log_request
end
private
def check_access
puts "Proxy: Checking access before making request."
# アクセス制御のロジックをここに実装
end
def log_request
puts "Proxy: Logging the request."
# ログの記録ロジックをここに実装
end
end
# クライアント
class Client
def initialize(subject) @subject = subject end
def make_request() @subject.request end
end
# クライアントの使用例
if __FILE__ == $0
real_subject = RealSubject.new
proxy = Proxy.new(real_subject)
client = Client.new(proxy)
client.make_request
# 出力:
# Proxy: Checking access before making request.
# RealSubject handles the request
# Proxy: Logging the request.
end
</syntaxhighlight>
この例では、<code>Proxy</code>が<code>RealSubject</code>へのアクセスを制御し、リクエスト前にアクセスをチェックし、リクエスト後にログを記録します。クライアントはプロキシと本物のオブジェクトを同様に扱うことができます。
:これらの構造デザインパターンは、クラスやオブジェクトの構造に関する課題に対処し、ソフトウェア設計の柔軟性や拡張性を向上させるために広く使用されています。それぞれのパターンは特定の課題に焦点を当て、適切な状況で使用されます。
=== 振る舞い(Behavioral)デザインパターン ===
振る舞いデザインパターンは、オブジェクト間の相互作用、責任の分散、およびアルゴリズムの柔軟な実装に焦点を当てたソフトウェア設計手法です。これらのパターンは、システムの結合度を低減し、拡張性と保守性を向上させることを目的としています。以下に、代表的な振る舞いデザインパターンの詳細と実践的なRubyでのコード例を示します。
==== Chain of Responsibility(責任の連鎖)パターン ====
Chain of Responsibilityパターンは、複数のオブジェクトがリクエストを処理する柔軟な連鎖メカニズムを提供します。リクエストは連鎖を順次渡され、各オブジェクトは自身で処理可能な場合はそれを処理し、できない場合は次のオブジェクトに委譲します。
:<syntaxhighlight lang=ruby copy>
# 抽象ハンドラクラス
class Handler
attr_accessor :next_handler
def handle_request(request)
handle(request) || forward_to_next(request)
end
private
def handle(request)
raise NotImplementedError, "#{self.class} has not implemented method 'handle'"
end
def forward_to_next(request)
@next_handler&.handle_request(request)
end
end
# 具体的なハンドラクラス
class LogHandler < Handler
private
def handle(request)
if request.type == :log
puts "ログを記録: #{request.content}"
true
else
false
end
end
end
class EmailHandler < Handler
private
def handle(request)
if request.type == :email
puts "メールを送信: #{request.content}"
true
else
false
end
end
end
class Request
attr_reader :type, :content
def initialize(type, content)
@type = type
@content = content
end
end
# クライアントコード
log_handler = LogHandler.new
email_handler = EmailHandler.new
log_handler.next_handler = email_handler
log_handler.handle_request(Request.new(:log, "システムエラーが発生"))
log_handler.handle_request(Request.new(:email, "重要な通知"))
</syntaxhighlight>
==== Command(コマンド)パターン ====
Commandパターンは、リクエストや操作をオブジェクトとしてカプセル化し、クライアントと処理の間の疎結合を実現します。これにより、コマンドの遅延実行、キュー、アンドゥ/リドゥ操作が容易に実装できます。
:<syntaxhighlight lang=ruby copy>
# レシーバークラス
class TextEditor
def copy = puts "テキストをコピー"
def paste = puts "テキストをペースト"
def cut = puts "テキストをカット"
end
# 抽象コマンドインターフェース
class Command
def execute = raise NotImplementedError
def undo = raise NotImplementedError
end
end
# 具体的なコマンドクラス
class CopyCommand < Command
def initialize(editor)
@editor = editor
end
def execute = @editor.copy
def undo
# コピー操作の取り消し(この例では特に何もしない)
end
end
# クライアントコード
editor = TextEditor.new
copy_cmd = CopyCommand.new(editor)
copy_cmd.execute
</syntaxhighlight>
==== Interpreter(インタープリター)パターン ====
Interpreterパターンは、特定のドメイン固有言語(DSL)の文法を解釈および実行するためのクラス群を提供します。
:<syntaxhighlight lang=ruby copy>
# 抽象式クラス
class Expression
def interpret(context)
raise NotImplementedError
end
end
# 端末式クラス
class NumberExpression < Expression
def initialize(number)
@number = number
end
def interpret(context)
context[@number.to_s]
end
end
# 非端末式クラス
class AddExpression < Expression
def initialize(left, right)
@left = left
@right = right
end
def interpret(context)
@left.interpret(context) + @right.interpret(context)
end
end
# クライアントコード
context = { '1' => 10, '2' => 20, '3' => 30 }
expression = AddExpression.new(
NumberExpression.new('1'),
AddExpression.new(NumberExpression.new('2'), NumberExpression.new('3'))
)
result = expression.interpret(context)
puts "計算結果: #{result}" # 出力: 計算結果: 60
</syntaxhighlight>
==== Iterator(イテレータ)パターン ====
Iterator(イテレータ)パターンは、オブジェクトの要素に順次アクセスするための方法を提供します。これにより、コレクション内の要素を順番にアクセスするための標準的な手法を提供し、同時にコレクションの内部構造を隠蔽します。
:<syntaxhighlight lang=ruby copy>
class CustomCollection
include Enumerable
def initialize() = @items = []
def add_item(item) = @items << item
def each(&block) = @items.each(&block)
end
collection = CustomCollection.new
collection.add_item("りんご")
collection.add_item("バナナ")
collection.add_item("オレンジ")
collection.each { |item| puts item }
</syntaxhighlight>
==== Mediator(仲介者)パターン ====
Mediator(仲介者)パターンは、オブジェクト間の相互作用をカプセル化し、オブジェクトが直接通信するのではなく、中央の仲介者を通じて通信する手法を提供します。これにより、オブジェクト間の結合度を低くし、変更に強いシステムを構築できます。
:<syntaxhighlight lang=ruby copy>
class ChatMediator
def initialize
@users = []
end
def add_user(user)
@users << user
end
def send_message(message, sender)
@users.each do |user|
user.receive(message) unless user == sender
end
end
end
class User
attr_reader :name
attr_writer :mediator
def initialize(name, mediator)
@name = name
@mediator = mediator
mediator.add_user(self)
end
def send(message)
puts "#{@name}: #{message}を送信"
@mediator.send_message(message, self)
end
def receive(message)
puts "#{@name}: #{message}を受信"
end
end
</syntaxhighlight>
==== Memento(メメント)パターン ====
Memento(メメント)パターンは、オブジェクトの状態を保存し、後で復元できるようにするパターンです。
:<syntaxhighlight lang=ruby copy>
class Originator
attr_accessor :state
def create_memento
Memento.new(@state)
end
def restore_from_memento(memento)
@state = memento.state
end
end
class Memento
attr_reader :state
def initialize(state)
@state = state
end
end
class Caretaker
def initialize
@mementos = []
end
def add_memento(memento)
@mementos << memento
end
def get_memento(index)
@mementos[index]
end
end
# 使用例
editor = Originator.new
history = Caretaker.new
editor.state = "初期状態"
history.add_memento(editor.create_memento)
editor.state = "変更後の状態"
history.add_memento(editor.create_memento)
editor.state = "最終状態"
# 以前の状態に戻す
editor.restore_from_memento(history.get_memento(1))
puts editor.state # "変更後の状態"
</syntaxhighlight>
==== Observer(観察者)パターン ====
Observer(観察者)パターンは、オブジェクトの状態変化を他のオブジェクトに通知するパターンです。
:<syntaxhighlight lang=ruby copy>
class Subject
def initialize
@observers = []
end
def add_observer(observer)
@observers << observer
end
def remove_observer(observer)
@observers.delete(observer)
end
def notify_observers(data)
@observers.each { |observer| observer.update(data) }
end
end
class WeatherStation < Subject
def initialize
super
@temperature = 0
end
def temperature=(new_temperature)
@temperature = new_temperature
notify_observers(@temperature)
end
end
class Observer
def update(temperature)
raise NotImplementedError, "#{self.class} must implement update"
end
end
class TemperatureDisplay < Observer
def update(temperature)
puts "温度ディスプレイ: #{temperature}度"
end
end
class Heater < Observer
def update(temperature)
if temperature < 20
puts "ヒーターをONにします"
else
puts "ヒーターをOFFにします"
end
end
end
# 使用例
weather_station = WeatherStation.new
display = TemperatureDisplay.new
heater = Heater.new
weather_station.add_observer(display)
weather_station.add_observer(heater)
weather_station.temperature = 15
weather_station.temperature = 25
</syntaxhighlight>
==== State(状態)パターン ====
State(状態)パターンは、オブジェクトの内部状態が変化したときに、そのオブジェクトの振る舞いも動的に変更できるようにするパターンです。
:<syntaxhighlight lang=ruby copy>
class TrafficLight
attr_accessor :state
def initialize
@state = RedState.new
end
def change
@state.change(self)
end
def current_color
@state.color
end
end
class TrafficLightState
def change(traffic_light)
raise NotImplementedError, "#{self.class} has not implemented method 'change'"
end
def color
raise NotImplementedError, "#{self.class} has not implemented method 'color'"
end
end
class RedState < TrafficLightState
def change(traffic_light)
puts "赤から黄色に変更"
traffic_light.state = YellowState.new
end
def color
"赤"
end
end
class YellowState < TrafficLightState
def change(traffic_light)
puts "黄色から緑に変更"
traffic_light.state = GreenState.new
end
def color
"黄色"
end
end
class GreenState < TrafficLightState
def change(traffic_light)
puts "緑から赤に変更"
traffic_light.state = RedState.new
end
def color
"緑"
end
end
# 使用例
traffic_light = TrafficLight.new
3.times do
puts "現在の色: #{traffic_light.current_color}"
traffic_light.change
end
</syntaxhighlight>
==== Strategy(戦略)パターン ====
Strategy(戦略)パターンは、アルゴリズムを実行時に切り替えられるようにするパターンです。
:<syntaxhighlight lang=ruby copy>
class Sorter
attr_writer :strategy
def initialize(strategy)
@strategy = strategy
end
def sort(data)
@strategy.sort(data)
end
end
class SortStrategy
def sort(data)
raise NotImplementedError, "#{self.class} has not implemented method 'sort'"
end
end
class BubbleSort < SortStrategy
def sort(data)
puts "バブルソートを実行"
data.sort
end
end
class QuickSort < SortStrategy
def sort(data)
puts "クイックソートを実行"
data.sort
end
end
# 使用例
data = [5, 2, 9, 1, 7]
sorter = Sorter.new(BubbleSort.new)
puts sorter.sort(data)
sorter.strategy = QuickSort.new
puts sorter.sort(data)
</syntaxhighlight>
==== Template Method(テンプレートメソッド)パターン ====
Template Method(テンプレートメソッド)パターンは、アルゴリズムの骨格を定義し、一部の手順をサブクラスで実装できるようにするパターンです。
:<syntaxhighlight lang=ruby copy>
class DataMiner
def mine
open_file
extract_data
parse_data
analyze_data
send_report
close_file
end
private
def open_file
raise NotImplementedError, "#{self.class} must implement open_file"
end
def extract_data
raise NotImplementedError, "#{self.class} must implement extract_data"
end
def parse_data
raise NotImplementedError, "#{self.class} must implement parse_data"
end
def analyze_data
puts "共通の分析処理を実行"
end
def send_report
puts "レポートを送信"
end
def close_file
puts "ファイルをクローズ"
end
end
class PDFDataMiner < DataMiner
private
def open_file
puts "PDFファイルを開く"
end
def extract_data
puts "PDFからデータを抽出"
end
def parse_data
puts "PDFデータをパース"
end
end
class CSVDataMiner < DataMiner
private
def open_file
puts "CSVファイルを開く"
end
def extract_data
puts "CSVからデータを抽出"
end
def parse_data
puts "CSVデータをパース"
end
end
# 使用例
pdf_miner = PDFDataMiner.new
pdf_miner.mine
csv_miner = CSVDataMiner.new
csv_miner.mine
</syntaxhighlight>
==== Visitor(訪問者)パターン ====
Visitor(訪問者)パターン:データ構造と処理を分離し、新しい操作を追加しやすくするパターンです。
:<syntaxhighlight lang=ruby copy>
module ShapeVisitor
def visit_circle(circle)
raise NotImplementedError, "#{self.class} must implement visit_circle"
end
def visit_rectangle(rectangle)
raise NotImplementedError, "#{self.class} must implement visit_rectangle"
end
end
class Shape
def accept(visitor)
raise NotImplementedError, "#{self.class} must implement accept"
end
end
class Circle < Shape
attr_reader :radius
def initialize(radius)
@radius = radius
end
def accept(visitor)
visitor.visit_circle(self)
end
end
class Rectangle < Shape
attr_reader :width, :height
def initialize(width, height)
@width = width
@height = height
end
def accept(visitor)
visitor.visit_rectangle(self)
end
end
class AreaCalculatorVisitor
include ShapeVisitor
def visit_circle(circle)
Math::PI * circle.radius ** 2
end
def visit_rectangle(rectangle)
rectangle.width * rectangle.height
end
end
class XMLExportVisitor
include ShapeVisitor
def visit_circle(circle)
"<circle radius='#{circle.radius}'/>"
end
def visit_rectangle(rectangle)
"<rectangle width='#{rectangle.width}' height='#{rectangle.height}'/>"
end
end
# 使用例
shapes = [Circle.new(5), Rectangle.new(3, 4)]
area_calculator = AreaCalculatorVisitor.new
xml_exporter = XMLExportVisitor.new
shapes.each do |shape|
puts "面積: #{shape.accept(area_calculator)}"
puts "XML: #{shape.accept(xml_exporter)}"
end
</syntaxhighlight>
:これらのデザインパターンは、ソフトウェア設計における複雑な問題に対する洗練された解決策を提供します。各パターンは特定の設計上の課題に焦点を当て、コードの柔軟性、拡張性、および保守性を向上させるための強力な手法を示しています。
== あとがき ==
デザインパターンは、ソフトウェアエンジニアリングの世界において実践的で効果的な解決策を提供するツールの一つです。本書を通じて、デザインパターンの基本概念や具体的なパターンについて学び、その利用方法やメリットについて理解を深めることができました。
デザインパターンを理解することは、ソフトウェア開発において優れた設計を行うための重要なスキルの一環です。これらのパターンは、再利用可能なコード、保守性の向上、柔軟性の確保など、さまざまな側面で開発者に利益をもたらします。一度習得したデザインパターンは、今後のプロジェクトやチームでのコーディングにおいて、より洗練された解決策を提供してくれることでしょう。
デザインパターンを適切に選択し、効果的に適用するためには、実際の開発プロジェクトでの経験が欠かせません。それによって、理論だけでなく実践的な洞察が得られ、より良いソフトウェアを構築する力が身につきます。
最後に、本書を通じてデザインパターンに触れてくださり、デザインの世界への探求心を共有できたことを嬉しく思います。今後もデザインパターンを活用し、より良いソフトウェアを作り出す旅が、より一層の洞察と発見をもたらすことでしょう。デザインパターンの素晴らしさと、その無限の可能性に向けて、新たなる冒険が始まりますように。
== 脚註 ==
<references />
{{DEFAULTSORT:てさいんはたん}}
[[Category:コンピュータ言語]]
[[Category:プログラミング|*]]
[[Category:計算機科学]]
[[Category:情報技術]]
{{NDC|007.64}}
al6pdkkljve11ipcuf0y37oesgrt8mf
264521
264520
2024-11-29T16:24:27Z
Ef3
694
/* あとがき */ 校閲と推敲と加筆
264521
wikitext
text/x-wiki
コンピューターサイエンスにおけるデザインパターンは、ソフトウェア設計において再利用可能な解決策の一般的なモデルやテンプレートです。これらのパターンは、共通の課題や問題に対処するために繰り返し使われ、設計の柔軟性、拡張性、保守性を向上させます。デザインパターンは、ソフトウェアエンジニアリングのコミュニティで広く受け入れられており、GoF(Gang of Four)による "Design Patterns: Elements Of Reusable Object-Oriented Software"(邦題『オブジェクト指向における再利用のためのデザインパターン』)などの文献で有名です。
デザインパターンの主な特徴は以下の通りです:
; 再利用性: デザインパターンは、共通の設計上の問題に対処するための汎用的な解決策を提供するため、再利用が容易です。同じまたは類似の課題に対して何度も設計を行う必要がなくなります。
; 柔軟性: デザインパターンは柔軟性をもたらし、変更が必要な場合にも容易に対応できるような設計を促進します。新しい要件や機能の追加があっても、デザインパターンを利用することで変更の影響を最小限に抑えられます。
; 共通の語彙: デザインパターンは、ソフトウェア開発者やエンジニアの間で共通の語彙を提供します。これにより、チーム内でのコミュニケーションが向上し、設計の理解が容易になります。
; ソフトウェアアーキテクチャ: デザインパターンはソフトウェアアーキテクチャを構築するための基本的な構成要素として使われます。これにより、ソフトウェアの構造が理解しやすく、拡張が容易になります。
一部の有名なデザインパターンには、Singleton(単一のインスタンスを保持するパターン)、Factory Method(ファクトリークラスがオブジェクトの生成を担当するパターン)、Observer(オブジェクトの状態変化を監視するパターン)などがあります。これらのパターンはソフトウェア設計の中で広く利用され、開発者が共通の問題に対してより効果的にアプローチできるようになります。
== デザインパターンの分類と概要 ==
デザインパターンは、ゴフ(Gang of Four)によって提案された23のパターンを、以下の3つの大きなカテゴリに分類できます。それぞれのカテゴリには、ソフトウェア開発における特定の課題に対処するためのパターンが含まれています。
=== 生成(Creational)デザインパターン ===
生成デザインパターンは、オブジェクトの生成に焦点を当て、オブジェクトの生成と構成に関する柔軟で再利用可能な手法を提供します。これらのパターンは、インスタンスの生成方法やクラスの初期化に関する課題に対処し、アプリケーションの柔軟性と保守性を向上させます。
==== Singleton(シングルトン)パターン ====
シングルトンパターンは、アプリケーション内で唯一のインスタンスを持つように設計されたデザインパターンです。このパターンは、クラスが単一のインスタンスしか持たないようにし、その唯一のインスタンスにアクセスするための手法を提供します。以下に、シングルトンパターンの基本的な説明とRubyでのコード例を示します。
; シングルトンパターンの特徴
# 単一のインスタンス:
#* シングルトンクラスは一つのインスタンスしか持ちません。
# グローバルアクセスポイント:
#* シングルトンインスタンスにアクセスするためのグローバルなアクセスポイントが提供されます。
# 共有リソース管理:
#* シングルトンパターンは、共有のリソースや設定にアクセスする場合に有用です。例えば、データベース接続やログ管理など。
; Rubyでのシングルトンパターンの実装(1)
:<syntaxhighlight lang=ruby>
class SingletonClass
# クラス変数で唯一のインスタンスを保持
@@instance = nil
# newメソッドをprivateにすることで、外部から直接インスタンス化できなくなる
private_class_method :new
# インスタンスにアクセスするためのメソッド
def self.instance() @@instance ||= new end
# その他のシングルトンクラスのメソッドやプロパティ
def some_method() puts "Singleton instance method called" end
end
# シングルトンクラスのインスタンスを取得
singleton_instance = SingletonClass.instance
singleton_instance.some_method
</syntaxhighlight>
この例では、<code>SingletonClass</code>クラスがシングルトンパターンを実現しています。<code>instance</code>メソッドを通じて唯一のインスタンスにアクセスできます。<code>new</code>メソッドは<code>private_class_method</code>でprivateになっており、外部から直接インスタンス化ができないようになっています。
Rubyには、シングルトンパターンをクラスで実践するために <code>Singleton</code> Mix-in が用意されています。
; Rubyでのシングルトンパターンの実装(2)
:<syntaxhighlight lang=ruby>
require 'singleton'
class SingletonClass
include Singleton
# その他のシングルトンクラスのメソッドやプロパティ
def some_method() puts "Singleton instance method called" end
end
# シングルトンクラスのインスタンスを取得
singleton_instance = SingletonClass.instance
singleton_instance.some_method
</syntaxhighlight>
この例では、<code>SingletonClass</code>クラスが<code>Singleton</code>モジュールを<code>include</code>しています。これにより、<code>SingletonClass</code>のインスタンスはシングルトンとして扱われ、<code>instance</code>メソッドで唯一のインスタンスにアクセスできます。
<code>singleton</code>モジュールを使用することで、シングルトンパターンの実装が簡潔になり、また必要なメソッドやプロパティを追加できます。シングルトンパターンは、共有リソースへのアクセスや設定管理などのシナリオで役立ちます。
このように実装されたシングルトンパターンでは、複数のインスタンスが作成されることを防ぎ、唯一の共有リソースへのアクセスを確実に制御できます。
==== Factory Method(ファクトリーメソッド)パターン ====
Factory Methodパターンは、オブジェクトの生成をサブクラスに委譲し、具体的な生成プロセスをサブクラスで実装する手法です。これにより、生成されるオブジェクトの型をサブクラスによって動的に変更できます。Factory Methodは、生成するオブジェクトの種類に依存せず、柔軟性を提供するために使用されます。
; Factory Method パターンの特徴
# 抽象クラスまたはインターフェース:
#* Factory Methodパターンでは、オブジェクトの生成を抽象クラスまたはインターフェースで定義します。
# サブクラスでの生成プロセス実装:
#* 具体的な生成プロセスはサブクラスに委譲され、サブクラスでそれぞれの生成方法が実装されます。
# 動的な型変更:
#* Factory Methodによって生成されるオブジェクトの型は、実行時にサブクラスの選択によって動的に変更できます。
; RubyでのFactory Method パターンの実装
: 以下は、Factory Methodパターンの簡単なコード例です。
:<syntaxhighlight lang=ruby>
# Product(生成されるオブジェクト)の抽象クラス
class Product
def operation() raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
end
# ConcreteProduct1(具体的な生成物1)
class ConcreteProduct1 < Product
def operation() "Operation from ConcreteProduct1" end
end
# ConcreteProduct2(具体的な生成物2)
class ConcreteProduct2 < Product
def operation() "Operation from ConcreteProduct2" end
end
# Creator(生成するオブジェクトの抽象クラス)
class Creator
def factory_method() raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
def some_operation()
product = factory_method
"Creator: #{product.operation}"
end
end
# ConcreteCreator1(具体的な生成者1)
class ConcreteCreator1 < Creator
def factory_method() ConcreteProduct1.new end
end
# ConcreteCreator2(具体的な生成者2)
class ConcreteCreator2 < Creator
def factory_method() ConcreteProduct2.new end
end
# クライアントコード
def client_code(creator) creator.some_operation end
# 具体的な生成者1を利用する場合
creator1 = ConcreteCreator1.new
puts client_code(creator1) # Output: "Creator: Operation from ConcreteProduct1"
# 具体的な生成者2を利用する場合
creator2 = ConcreteCreator2.new
puts client_code(creator2) # Output: "Creator: Operation from ConcreteProduct2"
</syntaxhighlight>
この例では、<code>Product</code>が生成されるオブジェクトを表す抽象クラスであり、<code>ConcreteProduct1</code>および<code>ConcreteProduct2</code>が具体的な生成物を表します。<code>Creator</code>が生成するオブジェクトの抽象クラスであり、<code>ConcreteCreator1</code>および<code>ConcreteCreator2</code>が具体的な生成者を表します。クライアントコードは、具体的な生成者を選択して生成物を得ることができます。
Factory Methodパターンを使用することで、クライアントコードは生成物の種類に依存せず、柔軟に生成プロセスを変更できるメリットがあります。
==== Abstract Factory(抽象ファクトリー)パターン ====
Abstract Factoryパターンは、関連する一連のオブジェクトを生成するためのインターフェースを提供し、一貫性を持たせます。異なるファクトリーを利用することで、異なるオブジェクトのファミリーを生成できます。このパターンは、オブジェクトが互いに関連しており、一緒に利用される場合に特に有用です。
; Abstract Factory パターンの要素
# AbstractFactory(抽象ファクトリー):
#* オブジェクトの生成に関するインターフェースを定義します。関連する一連のオブジェクトを生成するメソッドを提供します。
# ConcreteFactory(具体的なファクトリー):
#* <code>AbstractFactory</code>を実装し、具体的なオブジェクトの生成を行います。通常、同じファミリーのオブジェクトを生成します。
# AbstractProduct(抽象製品):
#* 生成されるオブジェクトの抽象クラスまたはインターフェースを定義します。
# ConcreteProduct(具体的な製品):
#* <code>AbstractProduct</code>を実装し、具体的なオブジェクトのクラスを提供します。
# Client(クライアント):
#* Abstract Factoryを利用してオブジェクトを生成します。クライアントは具体的なファクトリーを意識せずにオブジェクトを取得できます。
; Rubyでの Abstract Factory パターンの実装
: 以下は、Abstract Factoryパターンの簡単なコード例です。
:<syntaxhighlight lang=ruby>
# AbstractProduct
class Button
def click() raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
end
# ConcreteProduct1
class WindowsButton < Button
def click() "Windows button clicked" end
end
# ConcreteProduct2
class MacOSButton < Button
def click() "MacOS button clicked" end
end
# AbstractProduct
class Checkbox
def check() raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
end
# ConcreteProduct1
class WindowsCheckbox < Checkbox
def check() "Windows checkbox checked" end
end
# ConcreteProduct2
class MacOSCheckbox < Checkbox
def check() "MacOS checkbox checked" end
end
# AbstractFactory
class GUIFactory
def create_button() raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
def create_checkbox() raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
end
# ConcreteFactory1
class WindowsFactory < GUIFactory
def create_button() WindowsButton.new end
def create_checkbox() WindowsCheckbox.new end
end
# ConcreteFactory2
class MacOSFactory < GUIFactory
def create_button() MacOSButton.new end
def create_checkbox() MacOSCheckbox.new end
end
# Client
class Application
def initialize(factory)
@button = factory.create_button
@checkbox = factory.create_checkbox
end
def run()
puts @button.click
puts @checkbox.check
end
end
# クライアントコード
windows_app = Application.new(WindowsFactory.new)
macos_app = Application.new(MacOSFactory.new)
windows_app.run
# Output:
# "Windows button clicked"
# "Windows checkbox checked"
macos_app.run
# Output:
# "MacOS button clicked"
# "MacOS checkbox checked"
</syntaxhighlight>
この例では、WindowsおよびMacOS用のボタンとチェックボックスを生成する<code>GUIFactory</code>が抽象ファクトリーとなります。具体的な製品はそれぞれ<code>WindowsButton</code>、<code>WindowsCheckbox</code>、<code>MacOSButton</code>、<code>MacOSCheckbox</code>です。<code>Application</code>クラスが抽象ファクトリーを通じてオブジェクトを生成し、クライアントコードが異なるプラットフォーム用のオブジェクトを利用できます。
==== Builder(ビルダー)パターン ====
Builderパターンは、複雑なオブジェクトの構築プロセスを抽象化し、同じ構築プロセスを使用して異なる表現のオブジェクトを作成できるようにします。これにより、柔軟で再利用可能な構築手法を提供します。Builderパターンは、複雑なオブジェクトを一貫して構築する必要があり、同じ構築プロセスで異なるオブジェクトを生成する必要がある場合に有用です。
; Builder パターンの要素
# Product(製品):
#* ビルダーで構築されるオブジェクトの表現を定義します。
# Builder(ビルダー):
#* 複雑なオブジェクトを構築するための抽象インターフェースを提供します。具体的なビルダーがこのインターフェースを実装します。
# ConcreteBuilder(具体的なビルダー):
#* Builderを実装し、製品の各部分の構築を担当します。ビルドされた製品を取得するメソッドも提供します。
# Director(ディレクター):
#* クライアントからの指示に基づいてビルダーを利用してオブジェクトを構築します。ビルダーの組み合わせ方を知っています。
; Rubyでの Builder パターンの実装
: 以下は、Builderパターンの簡単なコード例です。
:<syntaxhighlight lang=ruby>
# Product
class Computer
attr_accessor :cpu, :memory, :storage
def to_s() "Computer with CPU: #{@cpu}, Memory: #{@memory}, Storage: #{@storage}" end
end
# Builder
class ComputerBuilder
def build_cpu() raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
def build_memory() raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
def build_storage() raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
def computer() raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
end
# ConcreteBuilder
class HighPerformanceComputerBuilder < ComputerBuilder
def initialize() @computer = Computer.new end
def build_cpu() @computer.cpu = "High Performance CPU" end
def build_memory() @computer.memory = "16GB RAM" end
def build_storage() @computer.storage = "1TB SSD" end
def computer() @computer end
end
# Director
class ComputerEngineer
def initialize(builder) @builder = builder end
def assemble_computer()
@builder.build_cpu
@builder.build_memory
@builder.build_storage
end
def get_computer() @builder.computer end
end
# クライアントコード
builder = HighPerformanceComputerBuilder.new
engineer = ComputerEngineer.new(builder)
engineer.assemble_computer
high_performance_computer = engineer.get_computer
puts high_performance_computer.to_s
# Output: "Computer with CPU: High Performance CPU, Memory: 16GB RAM, Storage: 1TB SSD"
</syntaxhighlight>
この例では、<code>Computer</code>がビルダーパターンで構築される製品を表しています。<code>ComputerBuilder</code>がビルダーの抽象クラスであり、<code>HighPerformanceComputerBuilder</code>が具体的なビルダーです。<code>ComputerEngineer</code>がディレクターで、ビルダーを利用して製品を組み立てます。クライアントコードはディレクターを通じてビルダーを利用して製品を構築し、最終的な製品を取得します。
このように、Builderパターンを使用することで、複雑なオブジェクトの構築プロセスをカプセル化し、柔軟かつ再利用可能な方法で異なる表現のオブジェクトを生成できます。
==== Prototype(プロトタイプ)パターン ====
Prototypeパターンは、オブジェクトのコピーを作成する手法を提供し、新しいオブジェクトを既存のオブジェクトを基にして生成できるようにします。これにより、オブジェクトの生成コストを削減できます。プロトタイプパターンは、同じようなオブジェクトが複数必要であり、それぞれのオブジェクトが少しだけ異なる場合に有用です。
; Prototype パターンの要素
# Prototype(プロトタイプ):
#* オブジェクトのコピーを作成するためのインターフェースを提供します。
# ConcretePrototype(具体的なプロトタイプ):
#* <code>Prototype</code>を実装し、オブジェクトのコピーを作成する具体的な実装を提供します。
# Client(クライアント):
#* プロトタイプを使用して新しいオブジェクトのコピーを作成します。
; Rubyでの Prototype パターンの実装
:以下は、Prototypeパターンの簡単なコード例です。
:<syntaxhighlight lang=ruby>
# Prototype
class Cloneable
def clone() raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
end
# ConcretePrototype
class ConcreteCloneable < Cloneable
attr_accessor :attribute
def initialize(attribute) @attribute = attribute end
def clone() self.class.new(@attribute) end
end
# Client
class ObjectCloner
def initialize(prototype) @prototype = prototype end
def clone_object() @prototype.clone end
end
# クライアントコード
original_object = ConcreteCloneable.new("Original Attribute")
cloner = ObjectCloner.new(original_object)
cloned_object = cloner.clone_object
puts "Original Object Attribute: #{original_object.attribute}"
puts "Cloned Object Attribute: #{cloned_object.attribute}"
# Output:
# Original Object Attribute: Original Attribute
# Cloned Object Attribute: Original Attribute
</syntaxhighlight>
この例では、<code>Cloneable</code>がプロトタイプの抽象クラスを表し、<code>ConcreteCloneable</code>が具体的なプロトタイプを表しています。<code>ObjectCloner</code>がクライアントで、指定されたプロトタイプを使用して新しいオブジェクトのコピーを作成します。クライアントコードは、元のオブジェクトとクローンされたオブジェクトの属性を出力しています。
このようにプロトタイプパターンを使用することで、オブジェクトの生成コストを低減し、柔軟性を向上させることができます。
:これらの生成デザインパターンは、オブジェクトの生成と初期化に関連する様々な課題に対処し、柔軟性や再利用性を向上させるために広く使用されています。それぞれのパターンは異なる状況や要件に対応し、効果的なソフトウェア設計を支援します。
=== 構造(Structural)デザインパターン ===
構造デザインパターンは、ソフトウェア設計においてクラスやオブジェクトの組み合わせを利用して大きな構造を形成する方法を提供します。これらのパターンは、ソフトウェアのコンポーネントを効果的に組み合わせ、機能や構造を拡張しやすくするための手法を提供します。
==== Adapter(アダプター)パターン ====
アダプターパターンは、互換性のないインターフェースを持つクラス同士を連携させるための手法を提供します。これにより、既存のクラスを変更せずに連携できるようになります。アダプターは、既存のクラスと新しいクラスの仲介を行います。
;クラスベースのアダプター
:クラスベースのアダプターパターンでは、既存のクラスを継承して新しいクラスを作成します。
:<syntaxhighlight lang=ruby>
# 既存のクラス
class LegacySystem
def specific_request() "Legacy System Request" end
end
# アダプタークラス
class Adapter < LegacySystem
def adapted_request() "Adapter: #{specific_request}" end
end
# クライアントコード
adapter = Adapter.new
puts adapter.adapted_request
# Output: "Adapter: Legacy System Request"
</syntaxhighlight>
;オブジェクトベースのアダプター
:オブジェクトベースのアダプターパターンでは、既存のクラスのオブジェクトを新しいクラスの内部で使用します。
:<syntaxhighlight lang=ruby>
# 既存のクラス
class LegacySystem
def specific_request() "Legacy System Request" end
end
# アダプタークラス
class Adapter
def initialize(legacy_system) @legacy_system = legacy_system end
def adapted_request() "Adapter: #{@legacy_system.specific_request}" end
end
# クライアントコード
legacy_system = LegacySystem.new
adapter = Adapter.new(legacy_system)
puts adapter.adapted_request
# Output: "Adapter: Legacy System Request"
</syntaxhighlight>
アダプターパターンは、既存のコードとの互換性を維持しながら新しいコードを導入する際に役立ちます。新しいクラスを既存のクラスと同じように振る舞わせることができ、クライアントコードは変更せずに利用できるようになります。
==== Bridge(ブリッジ)パターン ====
ブリッジパターンは、抽象化と実装を別々に拡張できるようにし、これらの間に橋渡しを行う手法を提供します。これにより、異なる抽象化と実装を組み合わせて新しい機能を追加できます。ブリッジパターンは、抽象クラスと実装クラスを分離して、それらが独立して拡張できるようにします。
;クラスベースのブリッジ
:クラスベースのブリッジパターンでは、抽象クラスと実装クラスがそれぞれ独立して拡張されます。
:<syntaxhighlight lang=ruby>
# 実装層
class Implementation
def operation_implementation() raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
end
# 具体的な実装クラス
class ConcreteImplementationA < Implementation
def operation_implementation() "Concrete Implementation A" end
end
class ConcreteImplementationB < Implementation
def operation_implementation() "Concrete Implementation B" end
end
# 抽象化
class Abstraction
def initialize(implementation) @implementation = implementation end
def operation() "Abstraction: #{implementation_operation}" end
private
def implementation_operation() @implementation.operation_implementation end
end
# クライアントコード
implementation_a = ConcreteImplementationA.new
abstraction_a = Abstraction.new(implementation_a)
puts abstraction_a.operation
# Output: "Abstraction: Concrete Implementation A"
implementation_b = ConcreteImplementationB.new
abstraction_b = Abstraction.new(implementation_b)
puts abstraction_b.operation
# Output: "Abstraction: Concrete Implementation B"
</syntaxhighlight>
;オブジェクトベースのブリッジ
:オブジェクトベースのブリッジパターンでは、抽象クラスと実装クラスをそれぞれオブジェクトとして組み合わせます。
:<syntaxhighlight lang=ruby>
# 実装層
class Implementation
def operation_implementation() raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
end
# 具体的な実装クラス
class ConcreteImplementationA < Implementation
def operation_implementation() "Concrete Implementation A" end
end
class ConcreteImplementationB < Implementation
def operation_implementation() "Concrete Implementation B" end
end
# 抽象化
class Abstraction
def initialize(implementation) @implementation = implementation end
def operation() "Abstraction: #{@implementation.operation_implementation}" end
end
# クライアントコード
implementation_a = ConcreteImplementationA.new
abstraction_a = Abstraction.new(implementation_a)
puts abstraction_a.operation
# Output: "Abstraction: Concrete Implementation A"
implementation_b = ConcreteImplementationB.new
abstraction_b = Abstraction.new(implementation_b)
puts abstraction_b.operation
# Output: "Abstraction: Concrete Implementation B"
</syntaxhighlight>
ブリッジパターンは、抽象化と実装の組み合わせが複雑になる場合や、将来的な変更に備えて柔軟性を持たせる場合に有用です。
==== Composite(コンポジット)パターン ====
コンポジットパターンは、オブジェクトとオブジェクトの構造を同一視し、個々のオブジェクトとその構造を再帰的に扱う手法を提供します。これにより、単一のオブジェクトと複数のオブジェクトの同一視が可能になります。このパターンは、個々のオブジェクトとオブジェクトのコンポジション(構造)を同じように扱い、クライアントが統一的に操作できるようにします。
; 特徴
# 同一視: コンポジットパターンでは、個々のオブジェクト(葉ノード)とオブジェクトの構造(複合ノード)を同じく扱います。これにより、単一のオブジェクトと複数のオブジェクトを同一視できます。
# 再帰的構造: 複合ノードは他の葉ノードや複合ノードを子に持つことができ、これが再帰的な構造を作ります。これにより、階層的で複雑な構造を表現できます。
# クライアントの簡略化: クライアントコードは、単一のオブジェクトとコンポジションを同じように扱うことができ、構造の詳細を気にせずに統一的な操作が可能です。
; 用途
# グラフィカルな構造: グラフィカルなツリーや図形の構造において、個々の要素とグループ(コンポジション)を同一視するのに適しています。
# ファイルシステム: ファイルやディレクトリといった構造を再帰的に扱う場合に利用されます。各要素はファイルかディレクトリかを同じように扱います。
# メニューシステム: GUIアプリケーションにおいて、メニュー項目とサブメニューを同様の要素として扱うのに適しています。
; コード例
:<syntaxhighlight lang=ruby>
# コンポーネント
class Component
def operation
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
end
# 葉ノード
class Leaf < Component
def operation() "Leaf" end
end
# 複合ノード
class Composite < Component
def initialize() @children = [] end
def add(child) @children << child end
def remove(child) @children.delete(child) end
def operation()
results = @children.map(&:operation).join(', ')
"Composite: [#{results}]"
end
end
# クライアントコード
leaf1 = Leaf.new
leaf2 = Leaf.new
composite = Composite.new
composite.add(leaf1)
composite.add(leaf2)
puts leaf1.operation
# Output: "Leaf"
puts composite.operation
# Output: "Composite: [Leaf, Leaf]"
</syntaxhighlight>
この例では、<code>Leaf</code>は葉ノードを、<code>Composite</code>は複合ノードを表しています。クライアントコードは、葉ノードと複合ノードを同様に扱っています。
==== Decorator(デコレーター)パターン ====
デコレーターパターンは、オブジェクトに新しい機能を動的に追加できるようにする手法を提供します。これにより、クラスを拡張する際に継承を使わずに機能を追加できます。デコレーターパターンは、既存のクラスを変更せずに新しい機能を追加する柔軟な手段を提供します。
; 特徴
# 柔軟な機能追加: デコレーターパターンは、既存のクラスを拡張せずに新しい機能を追加できます。これにより、クラスの変更が最小限に抑えられます。
# 再帰的なスタッキング: 複数のデコレーターを組み合わせて使用することができます。これにより、機能を再帰的にスタックすることが可能です。
# オープンクローズドの原則: 新しい機能は既存のコードを変更せずに追加できるため、オープンクローズドの原則をサポートします。
; 用途
# GUIコンポーネント: ウィンドウやボタンなどのGUIコンポーネントにおいて、外観や動作を動的に変更したい場合に利用されます。
# ファイルハンドリング: ファイルやストリームの処理において、データを暗号化する、圧縮するなどの機能を追加するのに適しています。
# ログ出力: ログ出力において、ログのフォーマットやレベルを動的に変更する際に利用されます。
; コード例
:<syntaxhighlight lang=ruby>
# コンポーネント
class Component
def operation() "Component" end
end
# デコレーター基底クラス
class Decorator < Component
def initialize(component) @component = component end
def operation() @component.operation end
end
# 具体的なデコレータークラス
class ConcreteDecoratorA < Decorator
def operation() "ConcreteDecoratorA: [#{super}]" end
end
class ConcreteDecoratorB < Decorator
def operation() "ConcreteDecoratorB: [#{super}]" end
end
# クライアントコード
component = Component.new
decorator_a = ConcreteDecoratorA.new(component)
decorator_b = ConcreteDecoratorB.new(decorator_a)
puts decorator_b.operation
# Output: "ConcreteDecoratorB: [ConcreteDecoratorA: [Component]]"
</syntaxhighlight>
この例では、<code>Component</code>が基本のコンポーネントを表し、<code>Decorator</code>がデコレーターの基底クラスを、<code>ConcreteDecoratorA</code>と<code>ConcreteDecoratorB</code>が具体的なデコレータークラスを表しています。クライアントコードは、これらのデコレーターを組み合わせて新しい機能を追加しています。
==== Facade(ファサード)パターン ====
ファサードパターンは、ソフトウェアデザインにおいて、サブシステムの一連のインターフェースを提供し、クライアントがサブシステムをより簡単に利用できるようにする手法を指します。このデザインパターンは、クライアントとサブシステムとの間に中間層を導入し、クライアントにとって使いやすい方法でサブシステムを操作できるようにします。これにより、クライアントはサブシステムの詳細に関与することなく、シンプルで一貫性のあるAPIを使用して機能を利用できます。
; 特徴:
:; 簡単なインターフェース提供: ファサードパターンは、サブシステム全体にわたる単一の簡潔なインターフェースを提供します。クライアントはこのファサードを通じてサブシステムを利用し、複雑な構造や相互作用を気にする必要がありません。
:; サブシステムの複雑性を隠蔽: ファサードはサブシステムの内部構造や複雑な操作をクライアントから隠蔽します。これにより、クライアントは必要な機能をシンプルなインターフェースを介して利用でき、サブシステムの詳細に気を取られることなくコードを記述できます。
:; カプセル化: サブシステムの各コンポーネントはファサードによってカプセル化され、外部からの直接アクセスを防ぎます。これにより、サブシステム内の変更が影響を及ぼす範囲が狭まります。
; 利点:
:; シンプルな利用: ファサードを使用することで、クライアントは複雑なサブシステムを理解せずにシンプルなインターフェースを利用できます。これにより、プログラムの理解や保守が容易になります。
:; 柔軟性と拡張性: サブシステムの変更や新しい機能の追加があっても、ファサードを変更するだけで済みます。これにより、システム全体の柔軟性と拡張性が向上します。
:; クライアントとサブシステムの分離: クライアントはサブシステムの詳細について知る必要がなくなります。ファサードによって、クライアントとサブシステムが疎結合になり、それぞれの変更が影響を及ぼしにくくなります。
音声処理システムのファサードパターンの例を示します。
:<syntaxhighlight lang=ruby>
# サブシステムの一部
class AudioPlayer
def play(file) puts "Playing audio file: #{file}" end
end
class AudioConverter
def convert(file, format) puts "Converting audio file #{file} to #{format}" end
end
class AudioMixer
def mix(files) puts "Mixing audio files: #{files.join(', ')}" end
end
# ファサード
class AudioFacade
def initialize()
@player = AudioPlayer.new
@converter = AudioConverter.new
@mixer = AudioMixer.new
end
def play_audio(file) @player.play(file) end
def convert_audio(file, format) @converter.convert(file, format) end
def mix_audio(files) @mixer.mix(files) end
end
# クライアント
if __FILE__ == $0
audio_facade = AudioFacade.new
# クライアントはファサードを通してサブシステムを利用
audio_facade.play_audio("song.mp3")
audio_facade.convert_audio("song.mp3", "wav")
audio_facade.mix_audio(["song1.wav", "song2.wav"])
end
</syntaxhighlight>
この例では、<code>AudioFacade</code>がファサードとして機能し、クライアントはこのファサードを介して音声処理システムを利用します。クライアントはサブシステムの詳細について心配する必要がなく、ファサードが提供する単純で使いやすいインターフェースを通じて操作を行います。
==== Flyweight(フライウェイト)パターン ====
フライウェイトパターンは、メモリ使用量を最適化するために多くの類似したオブジェクトを共有するデザインパターンです。このパターンでは、共有される部分とインスタンス固有の部分を分離し、共有される部分を再利用することで効率的なリソース利用を実現します。特に大規模なオブジェクトの集合を扱う場合に威力を発揮します。
; 特徴:
:; 共有: フライウェイトは、同じ状態を持つ複数のオブジェクトが共有されることによってメモリ使用量を最適化します。これにより、大量のオブジェクトが同じデータを保持する場合に効果を発揮します。
:; 状態の内外部化: フライウェイトは、内部状態(共有可能な部分)と外部状態(インスタンス固有の部分)を分離します。共有可能な部分は共有され、インスタンス固有の部分はインスタンスごとに持たれます。
:; イミュータブル: フライウェイトの内部状態は通常、イミュータブルであることが好まれます。これにより、共有状態が変更されることなく安全に共有できます。
; 利点:
:; メモリ効率: 類似のオブジェクトが共有されることで、メモリ使用量が減少し、効率的なリソース利用が可能になります。
:; インスタンスの生成コスト低減: インスタンスの生成がコストが高い場合、同じデータを持つオブジェクトを共有することで生成コストを低減できます。
:; 変更容易性: 共有状態が変更される場合、それが全ての参照先に影響を与えるため、変更が容易になります。
; フライウェイトパターンのコード例:
:<syntaxhighlight lang=ruby>
# フライウェイト
class SharedData
attr_reader :data
def initialize(data) @data = data end
end
# フライウェイトファクトリ
class FlyweightFactory
def initialize() @flyweights = {} end
def get_flyweight(key) @flyweights[key] ||= SharedData.new(key) end
def flyweights_count() @flyweights.length end
end
# クライアント
class Client
def initialize(factory)
@factory = factory
@flyweights = []
end
def get_or_create_flyweight(key)
flyweight = @factory.get_flyweight(key)
@flyweights.push(flyweight)
end
def report
puts "Number of unique flyweights: #{flyweights_count}"
end
private
def flyweights_count()
@flyweights.map(&:object_id).uniq.length
end
end
# クライアントの使用例
if __FILE__ == $0
factory = FlyweightFactory.new
client = Client.new(factory)
client.get_or_create_flyweight("A")
client.get_or_create_flyweight("B")
client.get_or_create_flyweight("A")
client.report
# 出力: Number of unique flyweights: 2
end
</syntaxhighlight>
この例では、<code>FlyweightFactory</code>が<code>SharedData</code>のインスタンスを共有し、<code>Client</code>がそれを利用しています。<code>Client</code>は同じデータを持つ<code>SharedData</code>を共有することで、効率的にメモリを利用しています。
==== Proxy(プロキシ)パターン ====
プロキシパターンは、別のオブジェクトにアクセスするためのプレースホルダーを提供し、そのアクセスを制御するデザインパターンです。主にアクセス制御、キャッシュ、遠隔アクセス、ログなどの機能を追加したり、本物のオブジェクトを生成する遅延初期化を行ったりする場合に使用されます。
; 特徴:
:; 制御機能の追加: プロキシは本物のオブジェクトへのアクセスを制御するため、特定の機能を追加することができます。例えば、アクセス制御、キャッシュ、遠隔アクセス、ログなど。
:; 遅延初期化: プロキシは本物のオブジェクトの生成を遅延させることができます。本物のオブジェクトが本当に必要になるまで生成を遅らせ、リソースの効率的な利用が可能です。
:; 単純なインターフェース: プロキシは本物のオブジェクトと同じインターフェースを提供します。クライアントはプロキシと本物のオブジェクトを同様に扱うことができます。
; 利点:
:; アクセス制御: プロキシは本物のオブジェクトへのアクセスを制御できるため、セキュリティや権限の管理が容易になります。
:; 遅延初期化: 本物のオブジェクトの生成を遅延させることで、リソースを効率的に利用できます。
:; 透過性: プロキシは本物のオブジェクトと同じインターフェースを提供するため、クライアントはプロキシと本物のオブジェクトを同様に扱うことができます。
; プロキシパターンのコード例:
:<syntaxhighlight lang=ruby>
# 本物のオブジェクト
class RealSubject
def request() puts "RealSubject handles the request" end
end
# プロキシ
class Proxy
def initialize(real_subject) @real_subject = real_subject end
def request
check_access
@real_subject.request
log_request
end
private
def check_access
puts "Proxy: Checking access before making request."
# アクセス制御のロジックをここに実装
end
def log_request
puts "Proxy: Logging the request."
# ログの記録ロジックをここに実装
end
end
# クライアント
class Client
def initialize(subject) @subject = subject end
def make_request() @subject.request end
end
# クライアントの使用例
if __FILE__ == $0
real_subject = RealSubject.new
proxy = Proxy.new(real_subject)
client = Client.new(proxy)
client.make_request
# 出力:
# Proxy: Checking access before making request.
# RealSubject handles the request
# Proxy: Logging the request.
end
</syntaxhighlight>
この例では、<code>Proxy</code>が<code>RealSubject</code>へのアクセスを制御し、リクエスト前にアクセスをチェックし、リクエスト後にログを記録します。クライアントはプロキシと本物のオブジェクトを同様に扱うことができます。
:これらの構造デザインパターンは、クラスやオブジェクトの構造に関する課題に対処し、ソフトウェア設計の柔軟性や拡張性を向上させるために広く使用されています。それぞれのパターンは特定の課題に焦点を当て、適切な状況で使用されます。
=== 振る舞い(Behavioral)デザインパターン ===
振る舞いデザインパターンは、オブジェクト間の相互作用、責任の分散、およびアルゴリズムの柔軟な実装に焦点を当てたソフトウェア設計手法です。これらのパターンは、システムの結合度を低減し、拡張性と保守性を向上させることを目的としています。以下に、代表的な振る舞いデザインパターンの詳細と実践的なRubyでのコード例を示します。
==== Chain of Responsibility(責任の連鎖)パターン ====
Chain of Responsibilityパターンは、複数のオブジェクトがリクエストを処理する柔軟な連鎖メカニズムを提供します。リクエストは連鎖を順次渡され、各オブジェクトは自身で処理可能な場合はそれを処理し、できない場合は次のオブジェクトに委譲します。
:<syntaxhighlight lang=ruby copy>
# 抽象ハンドラクラス
class Handler
attr_accessor :next_handler
def handle_request(request)
handle(request) || forward_to_next(request)
end
private
def handle(request)
raise NotImplementedError, "#{self.class} has not implemented method 'handle'"
end
def forward_to_next(request)
@next_handler&.handle_request(request)
end
end
# 具体的なハンドラクラス
class LogHandler < Handler
private
def handle(request)
if request.type == :log
puts "ログを記録: #{request.content}"
true
else
false
end
end
end
class EmailHandler < Handler
private
def handle(request)
if request.type == :email
puts "メールを送信: #{request.content}"
true
else
false
end
end
end
class Request
attr_reader :type, :content
def initialize(type, content)
@type = type
@content = content
end
end
# クライアントコード
log_handler = LogHandler.new
email_handler = EmailHandler.new
log_handler.next_handler = email_handler
log_handler.handle_request(Request.new(:log, "システムエラーが発生"))
log_handler.handle_request(Request.new(:email, "重要な通知"))
</syntaxhighlight>
==== Command(コマンド)パターン ====
Commandパターンは、リクエストや操作をオブジェクトとしてカプセル化し、クライアントと処理の間の疎結合を実現します。これにより、コマンドの遅延実行、キュー、アンドゥ/リドゥ操作が容易に実装できます。
:<syntaxhighlight lang=ruby copy>
# レシーバークラス
class TextEditor
def copy = puts "テキストをコピー"
def paste = puts "テキストをペースト"
def cut = puts "テキストをカット"
end
# 抽象コマンドインターフェース
class Command
def execute = raise NotImplementedError
def undo = raise NotImplementedError
end
end
# 具体的なコマンドクラス
class CopyCommand < Command
def initialize(editor)
@editor = editor
end
def execute = @editor.copy
def undo
# コピー操作の取り消し(この例では特に何もしない)
end
end
# クライアントコード
editor = TextEditor.new
copy_cmd = CopyCommand.new(editor)
copy_cmd.execute
</syntaxhighlight>
==== Interpreter(インタープリター)パターン ====
Interpreterパターンは、特定のドメイン固有言語(DSL)の文法を解釈および実行するためのクラス群を提供します。
:<syntaxhighlight lang=ruby copy>
# 抽象式クラス
class Expression
def interpret(context)
raise NotImplementedError
end
end
# 端末式クラス
class NumberExpression < Expression
def initialize(number)
@number = number
end
def interpret(context)
context[@number.to_s]
end
end
# 非端末式クラス
class AddExpression < Expression
def initialize(left, right)
@left = left
@right = right
end
def interpret(context)
@left.interpret(context) + @right.interpret(context)
end
end
# クライアントコード
context = { '1' => 10, '2' => 20, '3' => 30 }
expression = AddExpression.new(
NumberExpression.new('1'),
AddExpression.new(NumberExpression.new('2'), NumberExpression.new('3'))
)
result = expression.interpret(context)
puts "計算結果: #{result}" # 出力: 計算結果: 60
</syntaxhighlight>
==== Iterator(イテレータ)パターン ====
Iterator(イテレータ)パターンは、オブジェクトの要素に順次アクセスするための方法を提供します。これにより、コレクション内の要素を順番にアクセスするための標準的な手法を提供し、同時にコレクションの内部構造を隠蔽します。
:<syntaxhighlight lang=ruby copy>
class CustomCollection
include Enumerable
def initialize() = @items = []
def add_item(item) = @items << item
def each(&block) = @items.each(&block)
end
collection = CustomCollection.new
collection.add_item("りんご")
collection.add_item("バナナ")
collection.add_item("オレンジ")
collection.each { |item| puts item }
</syntaxhighlight>
==== Mediator(仲介者)パターン ====
Mediator(仲介者)パターンは、オブジェクト間の相互作用をカプセル化し、オブジェクトが直接通信するのではなく、中央の仲介者を通じて通信する手法を提供します。これにより、オブジェクト間の結合度を低くし、変更に強いシステムを構築できます。
:<syntaxhighlight lang=ruby copy>
class ChatMediator
def initialize
@users = []
end
def add_user(user)
@users << user
end
def send_message(message, sender)
@users.each do |user|
user.receive(message) unless user == sender
end
end
end
class User
attr_reader :name
attr_writer :mediator
def initialize(name, mediator)
@name = name
@mediator = mediator
mediator.add_user(self)
end
def send(message)
puts "#{@name}: #{message}を送信"
@mediator.send_message(message, self)
end
def receive(message)
puts "#{@name}: #{message}を受信"
end
end
</syntaxhighlight>
==== Memento(メメント)パターン ====
Memento(メメント)パターンは、オブジェクトの状態を保存し、後で復元できるようにするパターンです。
:<syntaxhighlight lang=ruby copy>
class Originator
attr_accessor :state
def create_memento
Memento.new(@state)
end
def restore_from_memento(memento)
@state = memento.state
end
end
class Memento
attr_reader :state
def initialize(state)
@state = state
end
end
class Caretaker
def initialize
@mementos = []
end
def add_memento(memento)
@mementos << memento
end
def get_memento(index)
@mementos[index]
end
end
# 使用例
editor = Originator.new
history = Caretaker.new
editor.state = "初期状態"
history.add_memento(editor.create_memento)
editor.state = "変更後の状態"
history.add_memento(editor.create_memento)
editor.state = "最終状態"
# 以前の状態に戻す
editor.restore_from_memento(history.get_memento(1))
puts editor.state # "変更後の状態"
</syntaxhighlight>
==== Observer(観察者)パターン ====
Observer(観察者)パターンは、オブジェクトの状態変化を他のオブジェクトに通知するパターンです。
:<syntaxhighlight lang=ruby copy>
class Subject
def initialize
@observers = []
end
def add_observer(observer)
@observers << observer
end
def remove_observer(observer)
@observers.delete(observer)
end
def notify_observers(data)
@observers.each { |observer| observer.update(data) }
end
end
class WeatherStation < Subject
def initialize
super
@temperature = 0
end
def temperature=(new_temperature)
@temperature = new_temperature
notify_observers(@temperature)
end
end
class Observer
def update(temperature)
raise NotImplementedError, "#{self.class} must implement update"
end
end
class TemperatureDisplay < Observer
def update(temperature)
puts "温度ディスプレイ: #{temperature}度"
end
end
class Heater < Observer
def update(temperature)
if temperature < 20
puts "ヒーターをONにします"
else
puts "ヒーターをOFFにします"
end
end
end
# 使用例
weather_station = WeatherStation.new
display = TemperatureDisplay.new
heater = Heater.new
weather_station.add_observer(display)
weather_station.add_observer(heater)
weather_station.temperature = 15
weather_station.temperature = 25
</syntaxhighlight>
==== State(状態)パターン ====
State(状態)パターンは、オブジェクトの内部状態が変化したときに、そのオブジェクトの振る舞いも動的に変更できるようにするパターンです。
:<syntaxhighlight lang=ruby copy>
class TrafficLight
attr_accessor :state
def initialize
@state = RedState.new
end
def change
@state.change(self)
end
def current_color
@state.color
end
end
class TrafficLightState
def change(traffic_light)
raise NotImplementedError, "#{self.class} has not implemented method 'change'"
end
def color
raise NotImplementedError, "#{self.class} has not implemented method 'color'"
end
end
class RedState < TrafficLightState
def change(traffic_light)
puts "赤から黄色に変更"
traffic_light.state = YellowState.new
end
def color
"赤"
end
end
class YellowState < TrafficLightState
def change(traffic_light)
puts "黄色から緑に変更"
traffic_light.state = GreenState.new
end
def color
"黄色"
end
end
class GreenState < TrafficLightState
def change(traffic_light)
puts "緑から赤に変更"
traffic_light.state = RedState.new
end
def color
"緑"
end
end
# 使用例
traffic_light = TrafficLight.new
3.times do
puts "現在の色: #{traffic_light.current_color}"
traffic_light.change
end
</syntaxhighlight>
==== Strategy(戦略)パターン ====
Strategy(戦略)パターンは、アルゴリズムを実行時に切り替えられるようにするパターンです。
:<syntaxhighlight lang=ruby copy>
class Sorter
attr_writer :strategy
def initialize(strategy)
@strategy = strategy
end
def sort(data)
@strategy.sort(data)
end
end
class SortStrategy
def sort(data)
raise NotImplementedError, "#{self.class} has not implemented method 'sort'"
end
end
class BubbleSort < SortStrategy
def sort(data)
puts "バブルソートを実行"
data.sort
end
end
class QuickSort < SortStrategy
def sort(data)
puts "クイックソートを実行"
data.sort
end
end
# 使用例
data = [5, 2, 9, 1, 7]
sorter = Sorter.new(BubbleSort.new)
puts sorter.sort(data)
sorter.strategy = QuickSort.new
puts sorter.sort(data)
</syntaxhighlight>
==== Template Method(テンプレートメソッド)パターン ====
Template Method(テンプレートメソッド)パターンは、アルゴリズムの骨格を定義し、一部の手順をサブクラスで実装できるようにするパターンです。
:<syntaxhighlight lang=ruby copy>
class DataMiner
def mine
open_file
extract_data
parse_data
analyze_data
send_report
close_file
end
private
def open_file
raise NotImplementedError, "#{self.class} must implement open_file"
end
def extract_data
raise NotImplementedError, "#{self.class} must implement extract_data"
end
def parse_data
raise NotImplementedError, "#{self.class} must implement parse_data"
end
def analyze_data
puts "共通の分析処理を実行"
end
def send_report
puts "レポートを送信"
end
def close_file
puts "ファイルをクローズ"
end
end
class PDFDataMiner < DataMiner
private
def open_file
puts "PDFファイルを開く"
end
def extract_data
puts "PDFからデータを抽出"
end
def parse_data
puts "PDFデータをパース"
end
end
class CSVDataMiner < DataMiner
private
def open_file
puts "CSVファイルを開く"
end
def extract_data
puts "CSVからデータを抽出"
end
def parse_data
puts "CSVデータをパース"
end
end
# 使用例
pdf_miner = PDFDataMiner.new
pdf_miner.mine
csv_miner = CSVDataMiner.new
csv_miner.mine
</syntaxhighlight>
==== Visitor(訪問者)パターン ====
Visitor(訪問者)パターン:データ構造と処理を分離し、新しい操作を追加しやすくするパターンです。
:<syntaxhighlight lang=ruby copy>
module ShapeVisitor
def visit_circle(circle)
raise NotImplementedError, "#{self.class} must implement visit_circle"
end
def visit_rectangle(rectangle)
raise NotImplementedError, "#{self.class} must implement visit_rectangle"
end
end
class Shape
def accept(visitor)
raise NotImplementedError, "#{self.class} must implement accept"
end
end
class Circle < Shape
attr_reader :radius
def initialize(radius)
@radius = radius
end
def accept(visitor)
visitor.visit_circle(self)
end
end
class Rectangle < Shape
attr_reader :width, :height
def initialize(width, height)
@width = width
@height = height
end
def accept(visitor)
visitor.visit_rectangle(self)
end
end
class AreaCalculatorVisitor
include ShapeVisitor
def visit_circle(circle)
Math::PI * circle.radius ** 2
end
def visit_rectangle(rectangle)
rectangle.width * rectangle.height
end
end
class XMLExportVisitor
include ShapeVisitor
def visit_circle(circle)
"<circle radius='#{circle.radius}'/>"
end
def visit_rectangle(rectangle)
"<rectangle width='#{rectangle.width}' height='#{rectangle.height}'/>"
end
end
# 使用例
shapes = [Circle.new(5), Rectangle.new(3, 4)]
area_calculator = AreaCalculatorVisitor.new
xml_exporter = XMLExportVisitor.new
shapes.each do |shape|
puts "面積: #{shape.accept(area_calculator)}"
puts "XML: #{shape.accept(xml_exporter)}"
end
</syntaxhighlight>
:これらのデザインパターンは、ソフトウェア設計における複雑な問題に対する洗練された解決策を提供します。各パターンは特定の設計上の課題に焦点を当て、コードの柔軟性、拡張性、および保守性を向上させるための強力な手法を示しています。
== あとがき ==
デザインパターンは、現代のソフトウェアエンジニアリングにおいて、複雑な設計課題を解決するための知的な道具箱と言えるでしょう。本書を通じて、デザインパターンの本質的な概念と、その実践的な適用方法について理解を深めることができたはずです。
デザインパターンは、単なる抽象的な概念ではなく、実際のソフトウェア開発において具体的な価値をもたらす強力な設計手法です。これらのパターンを理解し、適切に活用することで、以下のような重要な開発上の利点を得ることができます:
* コードの再利用性の向上
* システムの保守性と拡張性の改善
* オブジェクト間の疎結合の実現
* 共通の設計語彙の確立
* 複雑性の管理と抽象化
デザインパターンの真の力は、机上の理論ではなく、実践的な開発経験から生まれます。理論を学ぶことも重要ですが、実際のプロジェクトで試行錯誤しながらパターンを適用し、その効果と限界を理解することが、真の習熟への道となります。
パターンの適用において最も重要なのは、「万能の解決策」を求めないことです。各デザインパターンは、特定の文脈や課題に対して最適化されています。機械的に適用するのではなく、プロジェクトの具体的な要件と、コードの長期的な進化を常に意識することが求められます。
熟練のソフトウェアエンジニアは、デザインパターンを単なるテンプレートとしてではなく、柔軟な思考のためのツールキットとして捉えます。パターンは、創造的な問題解決のためのインスピレーションであり、盲目的に従うべき教義ではありません。
本書を通じて、デザインパターンへの探求を始めた読者の皆さんへ、一つの励ましの言葉を贈りたいと思います。デザインパターンの学習は、ソフトウェア設計における継続的な成長の旅です。失敗を恐れず、常に好奇心を持ち、新たな解決策を探求し続けてください。
今日学んだパターンは、明日の革新的なソリューションへの第一歩となるでしょう。デザインパターンとともに、より優れたソフトウェアを創造する旅に、皆さんが情熱と創造性を注ぐことを心から願っています。
:この本が、読者の皆さんのソフトウェア設計スキルへの探求において、一つの道標となれば幸いです。デザインパターンは終着点ではなく、新たな可能性への入り口なのです。
{{DEFAULTSORT:てさいんはたん}}
[[Category:プログラミング]]
== 脚註 ==
<references />
{{DEFAULTSORT:てさいんはたん}}
[[Category:コンピュータ言語]]
[[Category:プログラミング|*]]
[[Category:計算機科学]]
[[Category:情報技術]]
{{NDC|007.64}}
oe9fu2t9ad4j7byvy0fpvyc4s6kljy7
C++/Uniform initialization
0
39732
264548
263050
2024-11-30T06:16:20Z
Ef3
694
校閲と推敲と加筆
264548
wikitext
text/x-wiki
{{Nav}}
== Uniform Initializationの概要 ==
Uniform initializationは、C++11で導入された初期化方法の一つであり、ブレース <syntaxhighlight lang=c++ inline>{}</syntaxhighlight> を使用して異なる種類のオブジェクトを初期化する手法を指します。この方法は、さまざまな初期化シナリオで統一された構文を提供し、コードの一貫性や可読性を向上させることを目的としています。
=== Uniform Initializationとは何か? ===
Uniform initializationは、オブジェクトを初期化する際にブレース <syntaxhighlight lang=c++ inline>{}</syntaxhighlight> を使用する方法です。例えば、整数や浮動小数点数、配列、構造体、クラスなど、さまざまなデータ型やオブジェクトをこの方法で初期化することができます。この方法は、従来の初期化方法と比較して一貫性があり、初心者から上級者までの開発者にとって理解しやすい構文を提供します。
=== どのような問題を解決するのか? ===
Uniform initializationは、従来の初期化方法が持っていたいくつかの問題点を解決することを目指しています。従来の初期化方法では、初期化に使用される構文が多様であり、一貫性が欠けていました。また、一部の初期化方法では暗黙の型変換が発生し、意図しない挙動を引き起こすことがありました。Uniform initializationはこれらの問題を解決し、初期化の一貫性と安全性を向上させることを目指しています。
=== C++11で導入された背景や動機 ===
C++11では、Uniform initializationが導入された背景にはいくつかの要因があります。一つは、初期化の一貫性と可読性の向上です。従来の初期化方法では、開発者がそれぞれの初期化方法を覚える必要がありました。Uniform initializationは統一された初期化構文を提供することで、コードの可読性を向上させます。また、Uniform initializationは暗黙の型変換を防止し、初期化時の意図しない挙動を減らすことができます。これにより、プログラムの安全性が向上し、バグの発生を防ぐことができます。
== Uniform Initializationの基本的な文法 ==
Uniform initializationを使用すると、さまざまなオブジェクトを一貫した構文で初期化できます。この章では、Uniform initializationの基本的な文法について詳しく見ていきます。
=== ブレース <syntaxhighlight lang=c++ inline>{}</syntaxhighlight> を使用した初期化の基本形 ===
ブレース <syntaxhighlight lang=c++ inline>{}</syntaxhighlight> を使用した初期化は、Uniform initializationの基本形です。これは次のように使用します。
:<syntaxhighlight lang=c++>
// 整数の初期化
int x{10};
// 浮動小数点数の初期化
double y{3.14};
// 文字列の初期化
std::string str{"Hello"};
// 配列の初期化
int arr[]{1, 2, 3, 4, 5};
// 構造体の初期化
struct Point {
int x;
int y;
};
Point p{5, 10};
</syntaxhighlight>
このように、ブレース <syntaxhighlight lang=c++ inline>{}</syntaxhighlight> 内に初期化したい値を列挙することで、さまざまなオブジェクトを初期化できます。この方法は、従来の初期化方法と比較して一貫性があり、初心者から上級者までの開発者にとって理解しやすい構文を提供します。
=== 初期化リストを使った初期化方法の解説 ===
Uniform initializationでは、初期化リストを使って複数の値を一度に初期化することもできます。これは、ブレース <syntaxhighlight lang=c++ inline>{}</syntaxhighlight> 内に値をコンマで区切って列挙することで行います。
:<syntaxhighlight lang=c++>
// 初期化リストを使った初期化
std::vector<int> vec{1, 2, 3, 4, 5};
// 多次元配列の初期化
int matrix[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
// 構造体の初期化
struct Rectangle {
int width;
int height;
};
Rectangle rect{10, 20};
</syntaxhighlight>
初期化リストを使うことで、複数の値を効率的に初期化できます。また、初期化リストを使用すると、初期化したいオブジェクトの型に関係なく、統一された構文で初期化を行うことができます。
=== 型推論との組み合わせ ===
Uniform initializationは、型推論との組み合わせて使用することもできます。これにより、初期化時に型を明示的に指定する必要がなくなり、より簡潔なコードを書くことができます。
:<syntaxhighlight lang=c++>
// 型推論とUniform initializationの組み合わせ
auto x{10}; // int型として推論される
auto y{3.14}; // double型として推論される
auto str{"Hello"}; // const char*型として推論される
auto vec = std::vector<int>{1, 2, 3}; // std::vector<int>型として推論される
</syntaxhighlight>
型推論を使用することで、コードの記述量を減らし、初期化時の型の明示性を向上させることができます。Uniform initializationと型推論を組み合わせて使用することで、より効率的で読みやすいコードを書くことができます。
== Uniform Initializationの利点 ==
Uniform initializationは、さまざまな初期化シナリオでいくつかの重要な利点を提供します。ここでは、その利点について詳しく説明します。
=== 初期化リストの明確な構文 ===
Uniform initializationでは、初期化リストを使用することで、オブジェクトを初期化する際の構文が明確になります。ブレース <syntaxhighlight lang=c++ inline>{}</syntaxhighlight> を使用して値を列挙することで、初期化する値が明確に区別され、初期化の意図が明確に表現されます。
:<syntaxhighlight lang=c++>
// 初期化リストの明確な構文
std::vector<int> vec{1, 2, 3, 4, 5};
Rectangle rect{10, 20};
</syntaxhighlight>
このように、初期化リストを使用することで、初期化の構文が統一され、初心者から上級者までの開発者がコードを理解しやすくなります。
=== ブレース <syntaxhighlight lang=c++ inline>{}</syntaxhighlight> を使った初期化の一貫性 ===
Uniform initializationでは、どのようなオブジェクトでもブレース <syntaxhighlight lang=c++ inline>{}</syntaxhighlight> を使用して初期化できます。これにより、異なる種類のオブジェクトを初期化する際に、一貫した構文を使用することができます。
:<syntaxhighlight lang=c++>
// ブレース {}を使った初期化の一貫性
int x{10};
double y{3.14};
std::string str{"Hello"};
</syntaxhighlight>
このように、ブレース <syntaxhighlight lang=c++ inline>{}</syntaxhighlight> を使った初期化は、初期化方法を一貫させるための優れた手段です。
=== 暗黙の型変換を防止する安全性 ===
従来の初期化方法では、暗黙の型変換が発生し、意図しない挙動を引き起こすことがありました。しかし、Uniform initializationを使用すると、暗黙の型変換を防止できます。ブレース <syntaxhighlight lang=c++ inline>{}</syntaxhighlight> を使用することで、明示的な型変換が行われ、初期化の安全性が向上します。
このように、Uniform initializationは型変換を明示的に行うため、安全に初期化を行うことができます。
== 結論 ==
Uniform initializationは、C++11で導入された強力な機能であり、初期化を一貫性と安全性を持って行える方法を提供します。ブレース {} を使用した初期化は、さまざまなオブジェクトを初期化する際に役立ち、コードの可読性や一貫性を向上させます。今後、C++のコードを記述する際には、この方法を積極的に活用することをお勧めします。
{{Nav}}
{{DEFAULTSORT:UNIFORM INITIALIZATION}}
[[Category:C++]]
bcic1az53v8lilgei3twx6oq5hq3mqp
C++/C++開発環境の整備
0
39809
264543
251787
2024-11-30T05:05:02Z
Ef3
694
/* Windows向けC++開発環境の整備 */ ハードウェア要件などを更新
264543
wikitext
text/x-wiki
{{Nav}}
== C++開発環境の基本知識 ==
=== C++の概要と歴史 ===
C++は、Bjarne Stroustrupによって1983年に開発された汎用プログラミング言語であり、C言語にオブジェクト指向の概念を取り入れたものです。C++は、システムプログラミングやゲーム開発、デバイスドライバ、グラフィックスアプリケーションなど幅広い分野で使用されています。
2020年12月に規格出版された ISO/IEC 14882:2020(通称 C++20)では、モジュールやコルーチン、コンセプトなどの新機能が導入され、さらに表現力が向上しました。
=== C++開発環境の必要要素 ===
C++開発環境を整えるためには、以下の要素が必要です。
; コンパイラ : ソースコードを実行可能なバイナリに変換するツール。
; エディタ/IDE : コードの編集、デバッグ、ビルドを支援するツール。
; ビルドシステム : プロジェクトのビルドを管理するツール(例: CMake)。
; デバッガ : 実行中のプログラムを解析し、問題を見つけるツール。
; パッケージ管理ツール : 外部ライブラリの管理を行うツール(例: Conan、vcpkg)。
=== クロスプラットフォーム開発の利点と課題 ===
クロスプラットフォーム開発の主な利点は、同一のコードベースで複数のプラットフォームに対応できることです。これにより、開発コストや保守コストを削減できます。一方で、各プラットフォームの特有の問題や依存関係を管理する必要があるため、慎重な設計とテストが求められます。
----
== Windows向けC++開発環境の整備 ==
=== 必要なハードウェアとソフトウェアの要件 ===
; ハードウェア : 現代的なプロセッサ、16GB以上のRAM、SSD(十分な空き容量推奨)
; ソフトウェア : Windows 11、最新のWindows SDKおよび更新プログラム
=== Visual Studioのインストールと設定 ===
; Visual Studioのダウンロードとインストール
: [https://visualstudio.microsoft.com/ Visual Studioの公式サイト]からインストーラーをダウンロードします。
: インストーラーを実行し、「C++によるデスクトップ開発」および「CMakeツール」を選択してインストールします。
; Visual Studioの初期設定
: 起動時にテーマやワークロードをカスタマイズします。
: 「ツール」 -> 「オプション」 -> 「プロジェクトおよびソリューション」から必要な設定を確認・変更します。
; プロジェクトの作成
: 「新しいプロジェクトの作成」から「C++コンソールアプリケーション」を選択します。
: ソースコードファイルを追加し、簡単なC++プログラムを記述してコンパイルします。
; デバッグとビルド
: F5キーでデバッグモードを起動し、デバッグツールで変数やコールスタックを確認します。
=== MSYS2の導入と設定 ===
; MSYS2のインストール
: [https://www.msys2.org/ MSYS2公式サイト]からインストーラーをダウンロードしてインストールします。
: インストール後、<code>pacman</code>コマンドで最新状態に更新します。
::<syntaxhighligh lang=conosle>pacman -Syu</syntaxhighligh>
; 必要なツールチェインのインストール
: 以下のコマンドで必要なツールをインストールします。
::<syntaxhighligh lang=conosle>pacman -S base-devel mingw-w64-x86_64-toolchain</syntaxhighligh>
; 環境変数の設定
: MSYS2の<code>mingw64\bin</code>ディレクトリをシステムの<code>Path</code>環境変数に追加します。
=== VS Codeと他のエディタの使用方法 ===
; VS Codeのインストール
: [https://code.visualstudio.com/ VS Codeの公式サイト]からインストーラーをダウンロードしてインストールします。
; C++拡張機能の設定
: 「拡張機能」から「C++」拡張機能(Microsoft提供)をインストールします。
: <code>.vscode/settings.json</code>でインクルードパスやビルドタスクを設定します。
; 他のエディタの設定
: Sublime TextやCLionなどのエディタも公式ドキュメントに従って設定可能です。
=== Windows向け開発のベストプラクティス ===
; コードの整形とスタイルの統一 : <code>clang-format</code>でコードスタイルを統一します。
; バージョン管理の使用 : Gitを導入し、バージョン管理を徹底します。
; ビルドシステムの活用 : CMakeでクロスプラットフォームのビルド設定を管理します。
; CI/CDの導入 : GitHub ActionsやAzure Pipelinesで継続的インテグレーションを実現します。
----
== macOS向けC++開発環境の整備 ==
=== 必要なハードウェアとソフトウェアの要件 ===
; ハードウェア : MacBookやiMacなどのApple製デバイス、8GB以上のRAM、十分なディスクスペース
; ソフトウェア : macOS最新バージョン、Xcode、Homebrew
=== Xcodeのインストールと設定 ===
; Xcodeのダウンロードとインストール
: App StoreからXcodeをダウンロードし、インストールします。
; Xcodeの初期設定
: 初回起動時に、必要なコンポーネントが自動的にインストールされます。
: 「Preferences」 -> 「Locations」からコマンドラインツールの設定を確認します。
; プロジェクトの作成
: 「Create a new Xcode project」から「macOS」 -> 「Command Line Tool」を選択し、C++プロジェクトを作成します。
; デバッグとビルド
: Xcodeのビルドボタンを押してプログラムをビルドし、デバッグモードで実行します。
: デバッグコンソールで変数の値やコールスタックを確認します。
=== Homebrewを使用したツールチェインの整備 ===
; Homebrewのインストール
: ターミナルを開き、以下のコマンドを実行してHomebrewをインストールします。
:<syntaxhighlight lang=bash>
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
</syntaxhighlight>
; 必要なパッケージのインストール
: gccやcmakeなど、必要なツールをHomebrewを使ってインストールします。
:<syntaxhighlight lang=bash>
brew install gcc cmake
</syntaxhighlight>
=== VS CodeやCLionの設定 ===
; VS Codeのインストール
: [https://code.visualstudio.com/ VS Codeの公式サイト]からインストーラーをダウンロードし、インストールします。
; C++拡張機能の設定
: VS Codeを起動し、「拡張機能」パネルから「C++」拡張機能(Microsoft提供)をインストールします。
: 「settings.json」ファイルでインクルードパスやコンパイルコマンドを設定します。
; CLionのインストールと設定
: [https://www.jetbrains.com/clion/ CLionの公式サイト]からインストーラーをダウンロードし、インストールします。
: 初回起動時にライセンスの設定と初期設定を行います。
=== macOS向け開発のベストプラクティス ===
; クロスプラットフォームコードの記述 : POSIX標準APIを使用し、プラットフォーム依存コードを最小限にします。
; ユニットテストの導入 : Google TestやCatch2を使用してユニットテストを導入します。
; 継続的インテグレーション(CI) : GitHub ActionsやTravis CIを使用して、継続的インテグレーションを設定します。
----
== Linux向けC++開発環境の整備 ==
=== 必要なハードウェアとソフトウェアの要件 ===
; ハードウェア : モダンなプロセッサ、8GB以上のRAM、十分なディスクスペース
; ソフトウェア : 最新のLinuxディストリビューション(Debian, Fedoraなど)
=== gccやclangのインストールと設定 ===
; gccのインストール
: ターミナルを開き、以下のコマンドを実行してgccをインストールします(Debianの場合)。
:<syntaxhighlight lang=bash>
sudo apt update
sudo apt install build-essential
</syntaxhighlight>
; clangのインストール
: ターミナルを開き、以下のコマンドを実行してclangをインストールします(Debianの場合)。
:<syntaxhighlight lang=bash>
sudo apt install clang
</syntaxhighlight>
; コンパイルと実行
: 簡単なC++プログラムを作成し、以下のコマンドでコンパイルと実行を行います。
:<syntaxhighlight lang=bash>
g++ -o my_program my_program.cpp
./my_program
</syntaxhighlight>
=== 各種Linuxディストリビューション(Debian、Fedoraなど)での環境整備 ===
; Debian
: aptパッケージマネージャーを使用して必要なツールをインストールします。
; Fedora
: dnfパッケージマネージャーを使用して必要なツールをインストールします。
:<syntaxhighlight lang=bash>
sudo dnf install gcc-c++ make
</syntaxhighlight>
; その他のディストリビューション
: 各ディストリビューションの公式パッケージマネージャーを使用してツールをインストールします。
=== IDEの選定と設定(VS Code、CLion、Eclipseなど) ===
; VS Codeのインストール
: [https://code.visualstudio.com/ VS Codeの公式サイト]からインストーラーをダウンロードし、インストールします。
; C++拡張機能の設定
: VS Codeを起動し、「拡張機能」パネルから「C++」拡張機能(Microsoft提供)をインストールします。
: 「settings.json」ファイルでインクルードパスやコンパイルコマンドを設定します。
; CLionのインストールと設定
: [https://www.jetbrains.com/clion/ CLionの公式サイト]からインストーラーをダウンロードし、インストールします。
: 初回起動時にライセンスの設定と初期設定を行います。
; Eclipseのインストール
: [https://www.eclipse.org/ Eclipseの公式サイト]からインストーラーをダウンロードし、Eclipse IDE for C/C++ Developersを選択してインストールします。
=== Linux向け開発のベストプラクティス ===
; パッケージ管理の利用 : aptやdnfを使用して必要なライブラリを管理します。
; Makefileの活用 : Makefileを使用してビルドプロセスを自動化します。
; コードのテスト : Valgrindを使用してメモリリークやメモリ管理の問題を検出します。
----
== クラウド環境でのC++開発 ==
=== クラウド環境の概要と利点 ===
クラウド環境は、スケーラブルなコンピューティングリソースを提供し、インフラストラクチャの管理を簡素化します。これにより、リソースの効率的な使用、コストの削減、迅速なデプロイが可能になります。
=== AWS、Azure、GCPでのC++開発環境整備 ===
; AWSでの環境整備
:; EC2インスタンスの作成
:: AWS Management Consoleにアクセスし、EC2ダッシュボードから新しいインスタンスを作成します。
:: 必要な設定(インスタンスタイプ、ストレージ、セキュリティグループなど)を行い、インスタンスを起動します。
:; ツールのインストール
:: SSHを使用してインスタンスに接続し、必要な開発ツール(gcc, cmakeなど)をインストールします。
; Azureでの環境整備
:; 仮想マシンの作成
:: Azure Portalにアクセスし、新しい仮想マシンを作成します。
:: 必要な設定を行い、仮想マシンを起動します。
:; ツールのインストール
:: SSHを使用して仮想マシンに接続し、必要な開発ツールをインストールします。
; GCPでの環境整備
:; Compute Engineインスタンスの作成
:: GCP Consoleにアクセスし、新しいCompute Engineインスタンスを作成します。
:: 必要な設定を行い、インスタンスを起動します。
:; ツールのインストール
:: SSHを使用してインスタンスに接続し、必要な開発ツールをインストールします。
=== Dockerを使用したコンテナ化と環境設定 ===
; Dockerのインストール
: Docker公式サイトからインストーラーをダウンロードし、インストールします。
; Dockerfileの作成
: プロジェクトのルートディレクトリにDockerfileを作成し、開発環境を定義します。
:<syntaxhighlight lang=dockerfile>
FROM ubuntu:latest
RUN apt-get update && apt-get install -y build-essential cmake
WORKDIR /app
COPY . /app
</syntaxhighlight>
; Dockerイメージのビルドとコンテナの実行
: 以下のコマンドを実行してDockerイメージをビルドし、コンテナを起動します。
:<syntaxhighlight lang=bash>
docker build -t my_cpp_app .
docker run -it --rm my_cpp_app
</syntaxhighlight>
=== CI/CDパイプラインの構築 ===
; GitHub Actionsを使用したCI設定
: <code>.github/workflows</code>ディレクトリにCI用のワークフローファイルを作成します。
:<syntaxhighlight lang=yaml>
name: C++ CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Dependencies
run: sudo apt-get install -y build-essential cmake
- name: Build
run: |
cmake .
make
- name: Run Tests
run: ./tests
</syntaxhighlight>
; Travis CIを使用したCI設定
: プロジェクトルートに<code>.travis.yml</code>ファイルを作成し、CI設定を定義します。
:<syntaxhighlight lang=yaml>
language: cpp
compiler:
- gcc
- clang
before_install:
- sudo apt-get update -qq
- sudo apt-get install -y build-essential cmake
script:
- cmake .
- make
- ./tests
</syntaxhighlight>
=== クラウド開発のベストプラクティス ===
; インフラストラクチャのコード化 : TerraformやCloudFormationを使用してインフラをコードとして管理します。
; 自動スケーリング : 負荷に応じて自動的にリソースをスケーリングする設定を行います。
; セキュリティの確保 : 適切なアクセス制御とネットワークセキュリティを設定します。
----
== プラットフォームに依存しないツールとテクニック ==
=== バージョン管理システム(Gitなど) ===
; Gitのインストール
: 各プラットフォームに応じた方法でGit
をインストールします(例: <code>apt-get install git</code>)。
; 基本的なGitコマンド
: リポジトリの初期化: <code>git init</code>
: ファイルの追加とコミット: <code>git add .</code>、<code>git commit -m "初回コミット"</code>
: リモートリポジトリの設定: <code>git remote add origin <リポジトリURL></code>
: プッシュとプル: <code>git push origin master</code>、<code>git pull origin master</code>
=== テストフレームワーク(Google Test、Catch2など) ===
; Google Testのインストールと設定
: ソースコードをダウンロードし、ビルドします。
:<syntaxhighlight lang=bash>
git clone https://github.com/google/googletest.git
cd googletest
cmake .
make
sudo make install
</syntaxhighlight>
; 基本的なテストケースの作成
:<syntaxhighlight lang=cpp>
#include <gtest/gtest.h>
TEST(SampleTest, Test1) {
EXPECT_EQ(1, 1);
}
auto main(int argc, char **argv) -> int{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
</syntaxhighlight>
; Catch2のインストールと設定
: シングルヘッダーファイルをダウンロードし、プロジェクトに追加します。
:<syntaxhighlight lang=cpp>
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
TEST_CASE("Sample test case") {
REQUIRE(1 == 1);
}
</syntaxhighlight>
=== パッケージ管理(Conan、vcpkgなど) ===
; Conanのインストール
: pipを使用してConanをインストールします。
:<syntaxhighlight lang=bash>
pip install conan
</syntaxhighlight>
; Conanプロジェクトの設定
: <code>conanfile.txt</code>を作成し、必要なパッケージを定義します。
:<syntaxhighlight lang=text>
[requires]
gtest/1.10.0
[generators]
cmake
</syntaxhighlight>
; vcpkgのインストールと使用
: vcpkgをダウンロードし、ビルドします。
:<syntaxhighlight lang=bash>
git clone https://github.com/microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
</syntaxhighlight>
; パッケージのインストール
:<syntaxhighlight lang=bash>
./vcpkg install gtest
</syntaxhighlight>
=== クロスコンパイルと移植性の確保 ===
; クロスコンパイルの設定
: CMakeを使用してクロスコンパイルの設定を行います。
:<syntaxhighlight lang=cmake>
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_C_COMPILER arm-linux-gnueabi-gcc)
set(CMAKE_CXX_COMPILER arm-linux-gnueabi-g++)
</syntaxhighlight>
; 移植性のあるコードの書き方
: POSIX標準APIやBoostライブラリを使用して移植性を確保します。
: プラットフォーム依存のコードは条件付きコンパイルを使用して分離します。
<!----
== 付録 ==
=== よくあるトラブルシューティング ===
; コンパイルエラーの解決 : コンパイルエラーの原因と対策について詳述します。
; リンクエラーの解決 : リンクエラーの原因と対策について詳述します。
=== 参考書籍とオンラインリソース ===
; 参考書籍 : C++に関するおすすめの書籍を紹介します。
; オンラインリソース : 役立つウェブサイトやチュートリアルを紹介します。
=== コミュニティとサポートリソース ===
; コミュニティ : C++開発者向けのフォーラムやオンラインコミュニティを紹介します。
; サポートリソース : オンラインヘルプや公式ドキュメントのリンクを提供します。
---->
{{Nav}}
[[Category:C++|かいはつかんきよう の せいひ]]
mtdeqldjpoqnc0iujj0cscdbpta4ig7
Apache HTTP Server
0
40198
264514
264503
2024-11-29T12:30:24Z
Nermer314
62933
[[Special:Contributions/~2024-19669|~2024-19669]] ([[User talk:~2024-19669|トーク]]) による版 [[Special:Diff/264503|264503]] を取り消し
264514
wikitext
text/x-wiki
== Apache HTTP Serverの基礎 ==
=== Apache HTTP Serverとは ===
==== 基本機能とアーキテクチャ ====
Apache HTTP Server(Apache httpd)は、オープンソースのウェブサーバーソフトウェアであり、世界中で最も広く利用されています。最初のバージョンは1995年にリリースされ、それ以来、堅牢性、柔軟性、パフォーマンスで知られています。Apacheは、[[Hypertext Transfer Protocol]](HTTP)を使用してウェブページを提供するための機能を提供し、静的および動的なコンテンツの配信をサポートします。
Apache HTTP Serverの主な機能には以下が含まれます。
* '''モジュール方式のアーキテクチャ:''' Apacheはモジュール方式を採用しており、必要な機能を動的に追加することができます。例えば、セキュリティモジュール、キャッシュモジュール、リバースプロキシモジュールなどがあります。
* '''仮想ホストのサポート:''' 一つのサーバーで複数のウェブサイトをホストすることができ、ドメイン名やIPアドレスに基づいてリクエストを振り分けることができます。
* '''高度なログ機能:''' 詳細なアクセスログやエラーログを生成し、サーバーの動作状況を監視することができます。
==== 歴史と進化 ====
Apache HTTP Serverは、NCSA HTTPdプロジェクトを基にして1995年にリリースされました。名前の由来は「A Patchy Server」(多くのパッチを当てたサーバー)と言われています。プロジェクトは急速に成長し、Apache Software Foundation(ASF)によって管理されています。
* '''Apache 1.x:''' 最初のバージョンであり、シンプルでありながらも強力な機能を持っていました。
* '''Apache 2.x:''' 2002年にリリースされ、パフォーマンスの向上、マルチプロセッシングモジュール(MPM)の導入、より柔軟なモジュールシステムなどが特徴です。
=== インストール前の準備 ===
==== システム要件 ====
Apache HTTP Serverをインストールする前に、以下のシステム要件を確認してください。
* '''オペレーティングシステム:''' UNIX(FreeBSD、NetBSD、OpenBSD、macOS、Solarisなど)およびLinuxのディストリビューションなどのUNIXを模倣したOSやWindowsに対応
* '''ハードウェア:''' 最低限のリソースとして、1GHz以上のCPU、512MB以上のRAM、100MB以上のディスクスペースが推奨されます。ただし、実際のリソース要件はトラフィックや利用するモジュールによって変動します。
次章では、具体的なインストール手順について詳しく説明します。
== インストール ==
=== FreeBSDへのインストール ===
FreeBSDは、堅牢性とセキュリティに優れたUNIX系のオペレーティングシステムであり、サーバー用途に非常に適しています。この章では、FreeBSDでのApache HTTP Serverのインストール手順について説明します。
==== FreeBSD特有の注意点 ====
FreeBSDは、Ports Collectionと呼ばれる独自のパッケージ管理システムを持っており、ソースコードからのインストールを容易にする機能があります。また、pkgと呼ばれるPorts Collectionに基づいたバイナリパッケージ管理システムも提供されており、迅速なインストールが可能です。
==== インストール手順 ====
以下の作業はRoot権限が必要です。
===== システムの更新 =====
まず、システムのパッケージリストを最新の状態に更新します。
:<syntaxhighlight lang=csh>
pkg update && pkg upgrade
</syntaxhighlight>
===== Apache HTTP Serverのインストール =====
Apache HTTP Serverをインストールするためには、pkgコマンドを使用します。
:<syntaxhighlight lang=csh>
pkg install apache24
</syntaxhighlight>
<code>apache24</code>は、Apache HTTP Serverのバージョン2.4を指します。このコマンドを実行すると、必要な依存関係も自動的にインストールされます。
===== Apache HTTP Serverの起動と自動起動設定 =====
インストールが完了したら、Apacheを起動し、システム起動時に自動的に開始するように設定します。
:<syntaxhighlight lang=csh>
sysrc apache24_enable="YES"
service apache24 start
</syntaxhighlight>
<code>sysrc</code>コマンドは、FreeBSDのシステム設定を変更するためのコマンドで、<code>apache24_enable="YES"</code>と設定することで、システム起動時にApacheが自動的に起動するようになります。
===== ファイアウォールの設定 =====
必要に応じて、ファイアウォールの設定を確認し、HTTP(ポート80)およびHTTPS(ポート443)へのアクセスを許可します。pfを使用している場合の設定例を示します。
:<syntaxhighlight lang=csh>
vi /etc/pf.conf
</syntaxhighlight>
<code>/etc/pf.conf</code>に以下の行を追加します。
:<syntaxhighlight lang=text>
pass in on $ext_if proto tcp from any to any port { 80, 443 }
</syntaxhighlight>
設定を反映するためにpfを再読み込みします。
:<syntaxhighlight lang=csh>
pfctl -f /etc/pf.conf
pfctl -e
</syntaxhighlight>
===== Apacheの動作確認 =====
ブラウザを開き、サーバーのIPアドレスにアクセスしてApacheが正しく動作していることを確認します。例えば、<code>http://localhost</code>にアクセスします。Apacheのデフォルトページが表示されれば、インストールは正常に完了しています。
==== FreeBSD特有の設定 ====
FreeBSDには、いくつかのApache HTTP Serverの設定が特有です。
===== 設定ファイルの場所 =====
FreeBSDでは、Apacheの設定ファイルは以下のディレクトリに配置されています。
:<syntaxhighlight lang=csh>
/usr/local/etc/apache24/
</syntaxhighlight>
主な設定ファイルは<code>httpd.conf</code>ですが、<code>httpd.conf</code>は直接編集せず<code>httpd.conf</code>に読み込まれる設定ファイルをつかって設定します。
===== Apacheのドキュメントルート =====
デフォルトでは、ドキュメントルートは以下のディレクトリに設定されています。
:<syntaxhighlight lang=text>
/usr/local/www/apache24/data/
</syntaxhighlight>
必要に応じて、<code>httpd.conf</code>内でドキュメントルートを変更できます。
===== ログファイルの場所 =====
ログファイルは以下のディレクトリに配置されています。
:<syntaxhighlight lang=csh>
/var/log/httpd-access.log
/var/log/httpd-error.log
</syntaxhighlight>
=== まとめ ===
FreeBSDでのApache HTTP Serverのインストール手順を説明しました。次章では、Apache HTTP Serverの基本設定について詳しく説明します。
== 基本設定 ==
=== 初期設定ファイルの編集 ===
Apache HTTP Serverをインストールした後、適切な設定を行うことが重要です。ここでは、基本的な設定ファイルである<code>httpd.conf</code>の編集方法について説明します。
==== httpd.confの基本構造 ====
<code>httpd.conf</code>は、Apacheの主要な設定ファイルであり、サーバーの動作を制御するための指示が記述されています。FreeBSDでは、<code>httpd.conf</code>は以下のディレクトリにあります。
:<syntaxhighlight lang=text>
/usr/local/etc/apache24/httpd.conf
</syntaxhighlight>
pkg でインストールすると
:<syntaxhighlight lang=text>
/usr/local/etc/apache24/httpd.conf.sample
</syntaxhighlight>
に標準的な構成の設定ファイルがインストールされるので
:<syntaxhighlight lang=csh>
ln -s /usr/local/etc/apache24/httpd.conf.sample /usr/local/etc/apache24/httpd.conf
</syntaxhighlight>
とシンボリックリンクは張ります。
==== 主なディレクティブの説明 ====
以下は、<code>httpd.conf</code>の中で特に重要なディレクティブの説明です。
; ServerRoot :Apacheの基本ディレクトリを指定します。デフォルトは<code>/usr/local</code>です。
:<syntaxhighlight lang=apache>
ServerRoot "/usr/local"
</syntaxhighlight>
; Listen :Apacheが待ち受けるIPアドレスとポートを指定します。通常、ポート80を使用します。
:<syntaxhighlight lang=apache>
Listen 80
</syntaxhighlight>
; ServerAdmin :サーバー管理者のメールアドレスを指定します。エラーページなどで表示されます。
:<syntaxhighlight lang=apache>
ServerAdmin you@example.com
</syntaxhighlight>
; DocumentRoot :ウェブサイトのルートディレクトリを指定します。デフォルトは<code>/usr/local/www/apache24/data</code>です。
:<syntaxhighlight lang=apache>
DocumentRoot "/usr/local/www/apache24/data"
</syntaxhighlight>
; <Directory> :特定のディレクトリに対する設定を行います。DocumentRootの設定例を示します。
:<syntaxhighlight lang=apache>
<Directory "/usr/local/www/apache24/data">
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>
</syntaxhighlight>
; LoadModule :モジュールをロードするためのディレクティブです。例えば、<code>mod_rewrite</code>を有効にするには以下のように記述します。
:<syntaxhighlight lang=apache>
LoadModule rewrite_module libexec/apache24/mod_rewrite.so
</syntaxhighlight>
; Include :設定ファイルを読み込むためのディレクティブです。
:<syntaxhighlight lang=apache>
Include etc/apache24/Includes/*.conf
</syntaxhighlight>
この設定があるため、<code>/usr/local/etc/apache24/Includes/</code> にあり <code>.conf</code> で終わるファイルが自動的に読み込まれます。
これを利用して <code>/usr/local/etc/apache24/httpd.conf</code> 自体は編集せず、<code>/usr/local/etc/apache24/Includes/</code> 以下に <code>/usr/local/etc/apache24/extra/*.conf.sample</code> のシンボリックリンクを貼ったり、カスタム設定を書いた設定ファイルを置きます。
==== 仮想ホストの設定 ====
仮想ホストを使用することで、一つのサーバーで複数のドメインをホストできます。以下に、名前ベースの仮想ホストとIPベースの仮想ホストの設定例を示します。
===== 名前ベースの仮想ホスト =====
名前ベースの仮想ホストは、同じIPアドレスで複数のドメインをホストする場合に使用します。
;/usr/local/etc/apache24/Includes/namebase-vhosts.conf
:<syntaxhighlight lang=apache>
<VirtualHost *:80>
ServerAdmin admin@domain1.com
DocumentRoot "/usr/local/www/domain1"
ServerName www.domain1.com
ErrorLog "/var/log/domain1-error.log"
CustomLog "/var/log/domain1-access.log" common
</VirtualHost>
<VirtualHost *:80>
ServerAdmin admin@domain2.com
DocumentRoot "/usr/local/www/domain2"
ServerName www.domain2.com
ErrorLog "/var/log/domain2-error.log"
CustomLog "/var/log/domain2-access.log" common
</VirtualHost>
</syntaxhighlight>
===== IPベースの仮想ホスト =====
IPベースの仮想ホストは、異なるIPアドレスで異なるドメインをホストする場合に使用します。
;/usr/local/etc/apache24/Includes/ipaddrbase-vhosts.conf
:<syntaxhighlight lang=apache>
<VirtualHost 192.168.1.1:80>
ServerAdmin admin@domain1.com
DocumentRoot "/usr/local/www/domain1"
ServerName www.domain1.com
ErrorLog "/var/log/domain1-error.log"
CustomLog "/var/log/domain1-access.log" common
</VirtualHost>
<VirtualHost 192.168.1.2:80>
ServerAdmin admin@domain2.com
DocumentRoot "/usr/local/www/domain2"
ServerName www.domain2.com
ErrorLog "/var/log/domain2-error.log"
CustomLog "/var/log/domain2-access.log" common
</VirtualHost>
</syntaxhighlight>
==== 設定の反映と確認 ====
設定ファイルを編集した後、Apacheを再起動して変更を反映させます。
:<syntaxhighlight lang=csh>
service apache24 restart
</syntaxhighlight>
設定が正しく反映されたかどうかを確認するには、Apacheの構文チェックを行います。
:<syntaxhighlight lang=csh>
apachectl configtest
</syntaxhighlight>
エラーメッセージが表示されない場合、設定は正しく行われています。
=== まとめ ===
この章では、Apache HTTP Serverの基本設定について説明しました。次章では、セキュリティ設定について詳しく説明します。
== セキュリティ ==
ウェブサーバーのセキュリティは非常に重要です。適切な設定を行うことで、サーバーを外部の脅威から保護し、データの安全性を確保することができます。この章では、Apache HTTP Serverのセキュリティ設定について説明します。
=== 基本的なセキュリティ設定 ===
==== 権限とファイルアクセス制御 ====
Apacheを安全に運用するためには、適切な権限設定が重要です。以下のポイントに注意してください。
* '''ユーザーとグループ:''' Apacheは専用のユーザーとグループで動作するように設定します。通常、デフォルトでは<code>www</code>ユーザーと<code>www</code>グループが使用されます。<code>httpd.conf</code>で以下の設定を確認または変更します。
*:<syntaxhighlight lang=apache>
User www
Group www
</syntaxhighlight>
* '''ディレクトリのアクセス制御:''' <code><Directory></code>ディレクティブを使用して、特定のディレクトリに対するアクセスを制御します。以下の例は、DocumentRootディレクトリのアクセスを制御する設定です。
*:<syntaxhighlight lang=apache>
<Directory "/usr/local/www/apache24/data">
Options -Indexes +FollowSymLinks
AllowOverride None
Require all granted
</Directory>
</syntaxhighlight>
** <code>Options -Indexes</code>:ディレクトリのリスト表示を無効にします。
** <code>AllowOverride None</code>:.htaccessファイルによる設定の上書きを禁止します。
** <code>Require all granted</code>:すべてのリクエストを許可します。
==== SSL/TLSの設定 ====
SSL/TLSを使用して通信を暗号化することで、データの盗聴や改ざんを防ぐことができます。以下は、SSL/TLSの設定手順です。
===== OpenSSLのインストール =====
FreeBSDにはOpenSSLが標準でインストールされていますが、最新バージョンを使用するためにパッケージを更新します。
:<syntaxhighlight lang=csh>
pkg install openssl
</syntaxhighlight>
===== SSL証明書の作成 =====
自己署名証明書を作成するか、認証局(CA)から証明書を取得します。自己署名証明書を作成する手順は以下の通りです。
:<syntaxhighlight lang=csh>
openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 -keyout /usr/local/etc/apache24/server.key -out /usr/local/etc/apache24/server.crt
</syntaxhighlight>
===== SSLモジュールの有効化 =====
<code>httpd.conf</code>に以下の行を追加して、SSLモジュールを有効にします。
:<syntaxhighlight lang=apache>
LoadModule ssl_module libexec/apache24/mod_ssl.so
Include /usr/local/etc/apache24/extra/httpd-ssl.conf
</syntaxhighlight>
===== SSL設定の編集 =====
<code>httpd-ssl.conf</code>ファイルを編集して、SSL設定を行います。デフォルトの設定ファイルは以下にあります。
:<syntaxhighlight lang=csh>
vi /usr/local/etc/apache24/extra/httpd-ssl.conf
</syntaxhighlight>
以下の行を確認または変更します。
:<syntaxhighlight lang=apache>
<VirtualHost _default_:443>
DocumentRoot "/usr/local/www/apache24/data"
ServerName www.example.com:443
SSLEngine on
SSLCertificateFile "/usr/local/etc/apache24/server.crt"
SSLCertificateKeyFile "/usr/local/etc/apache24/server.key"
<FilesMatch "\.(cgi|shtml|phtml|php)$">
SSLOptions +StdEnvVars
</FilesMatch>
<Directory "/usr/local/www/apache24/cgi-bin">
SSLOptions +StdEnvVars
</Directory>
BrowserMatch "MSIE [2-6]" \
nokeepalive ssl-unclean-shutdown \
downgrade-1.0 force-response-1.0
CustomLog "/var/log/ssl_request_log" \
"%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
</VirtualHost>
</syntaxhighlight>
==== セキュリティモジュールの活用 ====
Apacheには、セキュリティを強化するためのモジュールが多数用意されています。ここでは、<code>mod_security</code>と<code>mod_evasive</code>について説明します。
===== mod_security =====
<code>mod_security</code>は、ウェブアプリケーションファイアウォール(WAF)として機能し、不正なリクエストをフィルタリングします。
:<syntaxhighlight lang=csh>
pkg install ap24-mod_security
</syntaxhighlight>
<code>httpd.conf</code>に以下の行を追加して、モジュールを有効にします。
:<syntaxhighlight lang=apache>
LoadModule security2_module libexec/apache24/mod_security2.so
Include /usr/local/etc/modsecurity/*.conf
</syntaxhighlight>
===== mod_evasive =====
<code>mod_evasive</code>は、DoS(Denial of Service)攻撃からサーバーを保護するためのモジュールです。
:<syntaxhighlight lang=csh>
pkg install ap24-mod_evasive
</syntaxhighlight>
<code>httpd.conf</code>に以下の行を追加して、モジュールを有効にします。
:<syntaxhighlight lang=apache>
LoadModule evasive20_module libexec/apache24/mod_evasive24.so
</syntaxhighlight>
以下は、<code>mod_evasive</code>の設定例です。
:<syntaxhighlight lang=apache>
<IfModule mod_evasive20.c>
DOSHashTableSize 3097
DOSPageCount 2
DOSSiteCount 50
DOSPageInterval 1
DOSSiteInterval 1
DOSBlockingPeriod 10
DOSEmailNotify you@example.com
DOSSystemCommand "su - someuser -c '/sbin/... %s ...'"
DOSLogDir "/var/log/mod_evasive"
</IfModule>
</syntaxhighlight>
=== まとめ ===
この章では、Apache HTTP Serverのセキュリティ設定について説明しました。次章では、パフォーマンス最適化について詳しく説明します。
== パフォーマンス最適化 ==
ウェブサーバーのパフォーマンスを最適化することは、ユーザーエクスペリエンスを向上させ、サーバーのリソースを効率的に利用するために重要です。この章では、Apache HTTP Serverのパフォーマンスを最適化する方法について説明します。
=== 基本的な最適化設定 ===
==== マルチプロセッシングモジュール(MPM)の選択 ====
Apache HTTP Serverは、複数のマルチプロセッシングモジュール(MPM)を提供しており、サーバーのリクエスト処理方式を選択できます。最も一般的なMPMは以下の通りです。
; prefork :各リクエストを個別のプロセスで処理します。安定性が高いが、メモリ消費が多い。
; worker :各プロセスが複数のスレッドを持ち、リクエストを並行処理します。効率的でスケーラブル。
; event :worker MPMに基づいており、Keep-Aliveリクエストの処理を最適化します。高い並行処理能力を持つ。
<code>httpd.conf</code>でMPMを設定します。例えば、worker MPMを使用する場合:
:<syntaxhighlight lang=apache>
LoadModule mpm_worker_module libexec/apache24/mod_mpm_worker.so
</syntaxhighlight>
==== Keep-Aliveの設定 ====
Keep-Aliveを有効にすると、複数のリクエストを同じ接続で処理でき、パフォーマンスが向上します。ただし、Keep-Alive接続の数やタイムアウト時間を適切に設定することが重要です。
:<syntaxhighlight lang=apache>
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5
</syntaxhighlight>
==== コンテンツ圧縮 ====
コンテンツを圧縮することで、帯域幅の使用量を削減し、ページロード時間を短縮できます。mod_deflateモジュールを使用してコンテンツを圧縮します。
:<syntaxhighlight lang=apache>
LoadModule deflate_module libexec/apache24/mod_deflate.so
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css application/javascript
</IfModule>
</syntaxhighlight>
=== キャッシングの活用 ===
キャッシングを活用することで、サーバーの負荷を軽減し、レスポンス時間を短縮できます。Apache HTTP Serverは、複数のキャッシングモジュールを提供しています。
==== mod_cacheとmod_cache_disk ====
<code>mod_cache</code>と<code>mod_cache_disk</code>を使用してディスクベースのキャッシングを設定します。
:<syntaxhighlight lang=apache>
LoadModule cache_module libexec/apache24/mod_cache.so
LoadModule cache_disk_module libexec/apache24/mod_cache_disk.so
<IfModule mod_cache.c>
<IfModule mod_cache_disk.c>
CacheRoot "/var/cache/apache2/mod_cache_disk"
CacheEnable disk "/"
CacheDirLevels 2
CacheDirLength 1
</IfModule>
</IfModule>
</syntaxhighlight>
==== mod_expires ====
<code>mod_expires</code>を使用して、キャッシュ有効期限を設定します。
:<syntaxhighlight lang=apache>
LoadModule expires_module libexec/apache24/mod_expires.so
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault "access plus 1 day"
ExpiresByType text/html "access plus 1 hour"
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
</IfModule>
</syntaxhighlight>
=== データベース接続の最適化 ===
ウェブサーバーとデータベースの連携は、ウェブアプリケーションのパフォーマンスに大きく影響します。以下のポイントに注意してデータベース接続を最適化します。
* '''持続的接続の使用:''' データベースとの持続的接続を確立することで、接続オーバーヘッドを削減します。
* '''接続プールの利用:''' データベース接続プールを利用して、同時接続数を制御し、リソースの効率的な利用を図ります。
* '''クエリの最適化:''' データベースクエリを最適化し、インデックスを適切に使用することで、データベースのレスポンス時間を短縮します。
=== ロードバランシング ===
高トラフィックなウェブサイトでは、ロードバランシングを導入することで、複数のサーバーにリクエストを分散し、パフォーマンスを向上させます。Apache HTTP Serverには、<code>mod_proxy_balancer</code>モジュールを使用してロードバランシングを設定できます。
:<syntaxhighlight lang=apache>
LoadModule proxy_module libexec/apache24/mod_proxy.so
LoadModule proxy_balancer_module libexec/apache24/mod_proxy_balancer.so
LoadModule lbmethod_byrequests_module libexec/apache24/mod_lbmethod_byrequests.so
<Proxy "balancer://mycluster">
BalancerMember "http://backend1.example.com:80"
BalancerMember "http://backend2.example.com:80"
ProxySet lbmethod=byrequests
</Proxy>
ProxyPass "/balancer" "balancer://mycluster"
ProxyPassReverse "/balancer" "balancer://mycluster"
</syntaxhighlight>
=== まとめ ===
この章では、Apache HTTP Serverのパフォーマンスを最適化するための設定と手法について説明しました。次章では、ログ管理とモニタリングについて詳しく説明します。
== ログ管理とモニタリング ==
効果的なログ管理とモニタリングは、サーバーの健全性を維持し、問題を迅速に特定・解決するために不可欠です。この章では、Apache HTTP Serverのログ管理とモニタリングの設定について説明します。
=== ログの基本設定 ===
Apache HTTP Serverは、アクセスログとエラーログの2種類のログを提供しています。これらのログファイルは、<code>httpd.conf</code>で設定します。
==== アクセスログ ====
アクセスログには、クライアントのリクエスト情報が記録されます。デフォルトの設定は以下の通りです。
:<syntaxhighlight lang=apache>
CustomLog "/var/log/httpd-access.log" common
</syntaxhighlight>
この設定は、アクセスログを<code>/var/log/httpd-access.log</code>に記録し、ログフォーマットを「common」に設定しています。ログフォーマットは<code>LogFormat</code>ディレクティブを使用してカスタマイズできます。
:<syntaxhighlight lang=apache>
LogFormat "%h %l %u %t \"%r\" %>s %b" common
</syntaxhighlight>
* <code>%h</code>: リモートホスト
* <code>%l</code>: リモートログ名(identdを使用)
* <code>%u</code>: リモートユーザー(HTTP認証を使用)
* <code>%t</code>: リクエストの時刻
* <code>\"%r\"</code>: リクエストの最初の行
* <code>%>s</code>: ステータスコード
* <code>%b</code>: 送信されたバイト数
==== エラーログ ====
エラーログには、サーバーエラーや警告メッセージが記録されます。デフォルトの設定は以下の通りです。
:<syntaxhighlight lang=apache>
ErrorLog "/var/log/httpd-error.log"
</syntaxhighlight>
エラーログのレベルは<code>LogLevel</code>ディレクティブを使用して設定できます。
:<syntaxhighlight lang=apache>
LogLevel warn
</syntaxhighlight>
* '''LogLevel'''の主なオプション:
** <code>emerg</code>: 緊急(システムが使用不能)
** <code>alert</code>: 直ちに修正が必要
** <code>crit</code>: 致命的な状態
** <code>error</code>: エラー
** <code>warn</code>: 警告
** <code>notice</code>: 通常よりも重要な情報
** <code>info</code>: 情報
** <code>debug</code>: デバッグメッセージ
=== ログのローテーション ===
ログファイルが大きくなりすぎると、管理が困難になります。ログローテーションを設定することで、ログファイルを一定期間ごとに分割・アーカイブできます。FreeBSDでは、<code>newsyslog</code>を使用してログローテーションを設定します。
==== newsyslogの設定 ====
<code>/etc/newsyslog.conf</code>ファイルに、Apacheのログファイルのローテーション設定を追加します。
:<syntaxhighlight lang=csh>
vi /etc/newsyslog.conf
</syntaxhighlight>
以下の行を追加します。
:<syntaxhighlight lang=text>
/var/log/httpd-access.log 644 7 100 * Z
/var/log/httpd-error.log 644 7 100 * Z
</syntaxhighlight>
* <code>644</code>: ファイルのパーミッション
* <code>7</code>: 保持するログファイルの数
* <code>100</code>: ログファイルの最大サイズ(KB単位)
* <code>*</code>: ログローテーションのタイミング(<code>*</code>はサイズに基づくローテーション)
* <code>Z</code>: 圧縮オプション(gzip)
=== モニタリング ===
Apache HTTP Serverのモニタリングは、サーバーのパフォーマンスと健全性をリアルタイムで監視するために重要です。
==== mod_status ====
<code>mod_status</code>モジュールを使用して、サーバーのステータス情報を取得できます。<code>mod_status</code>を有効にするには、<code>httpd.conf</code>に以下の行を追加します。
:<syntaxhighlight lang=apache>
LoadModule status_module libexec/apache24/mod_status.so
<Location "/server-status">
SetHandler server-status
Require host example.com
</Location>
</syntaxhighlight>
これで、<code>http://localhost/server-status</code>にアクセスすると、サーバーのステータス情報が表示されます。
==== 外部モニタリングツール ====
外部モニタリングツールを使用することで、より詳細な解析とアラート機能を利用できます。以下は、代表的なモニタリングツールです。
; Nagios :オープンソースのモニタリングツールで、サーバーやネットワークの監視に広く利用されています。
; Zabbix :Nagiosと同様に、オープンソースで強力なモニタリングツールです。多機能で拡張性が高い。
; New Relic :クラウドベースのモニタリングサービスで、アプリケーションパフォーマンスの監視に特化しています。
=== アラートの設定 ===
サーバーの状態に応じてアラートを設定することで、問題が発生した際に迅速に対応できます。外部モニタリングツールには、カスタムアラートを設定する機能が含まれています。
==== Nagiosでのアラート設定 ====
NagiosでApache HTTP Serverのアラートを設定する例を示します。まず、Apacheのプラグインをインストールします。
:<syntaxhighlight lang=csh>
pkg install nagios-plugins-apache
</syntaxhighlight>
次に、<code>/usr/local/etc/nagios/objects/commands.cfg</code>にApacheのチェックコマンドを追加します。
:<syntaxhighlight lang=text>
define command {
command_name check_apache
command_line /usr/local/libexec/nagios/check_http -H localhost
}
</syntaxhighlight>
最後に、<code>/usr/local/etc/nagios/objects/localhost.cfg</code>にサービス定義を追加します。
:<syntaxhighlight lang=text>
define service {
use generic-service
host_name localhost
service_description Apache
check_command check_apache
}
</syntaxhighlight>
=== まとめ ===
この章では、Apache HTTP Serverのログ管理とモニタリングについて説明しました。これにより、サーバーの健全性を維持し、問題を迅速に特定・解決するための基盤が整います。次章では、トラブルシューティングとメンテナンスについて詳しく説明します。
== トラブルシューティングとメンテナンス ==
ウェブサーバーの運用において、トラブルシューティングと定期的なメンテナンスは重要な作業です。この章では、Apache HTTP Serverのトラブルシューティング手法とメンテナンスのベストプラクティスについて説明します。
=== トラブルシューティング ===
==== エラーログの解析 ====
エラーログは、サーバーで発生した問題の詳細な情報を提供します。<code>/var/log/httpd-error.log</code>に記録されるエラーログを定期的に確認し、問題の原因を特定します。
:<syntaxhighlight lang=csh>
tail -f /var/log/httpd-error.log
</syntaxhighlight>
==== サービスの状態確認 ====
Apache HTTP Serverのサービスが正しく動作しているか確認するために、以下のコマンドを使用します。
:<syntaxhighlight lang=csh>
service apache24 status
</syntaxhighlight>
==== 設定ファイルのチェック ====
設定ファイルに誤りがないか確認するために、<code>apachectl</code>コマンドを使用します。
:<syntaxhighlight lang=csh>
apachectl configtest
</syntaxhighlight>
エラーが発生した場合は、<code>httpd.conf</code>ファイルを修正し、再度確認します。
==== ポートの確認 ====
Apache HTTP Serverが正しいポートでリスニングしているか確認します。
:<syntaxhighlight lang=csh>
sockstat -4 -6 | grep httpd
</syntaxhighlight>
出力に指定したポートが含まれていることを確認します。
==== モジュールのロード状況確認 ====
Apacheが必要なモジュールを正しくロードしているか確認します。<code>httpd -M</code>コマンドを使用すると、ロードされているモジュールの一覧が表示されます。
:<syntaxhighlight lang=csh>
httpd -M
</syntaxhighlight>
=== よくある問題と対処法 ===
==== サーバーが起動しない ====
; エラーログの確認 :<code>/var/log/httpd-error.log</code>を確認し、エラーメッセージをチェックします。
; 設定ファイルの確認 :設定ファイルに文法エラーがないか確認します。<code>apachectl configtest</code>を使用して設定ファイルをチェックします。
==== ページが表示されない ====
; アクセスログの確認 :<code>/var/log/httpd-access.log</code>を確認し、リクエストがサーバーに到達しているか確認します。
; ファイルのパーミッション :サーバーがドキュメントルート内のファイルにアクセスできるか確認します。
; 仮想ホストの設定 :仮想ホストの設定が正しいか確認します。特に<code>ServerName</code>や<code>DocumentRoot</code>の設定に注意します。
==== パフォーマンスの低下 ====
; リソースの監視 :サーバーのCPUやメモリの使用状況を監視し、ボトルネックを特定します。<code>top</code>コマンドや<code>htop</code>コマンドを使用します。
; ログの確認 :アクセスログやエラーログを確認し、異常なリクエストやエラーメッセージがないか確認します。
; 設定の見直し :Keep-AliveやMPMの設定を見直し、最適化を行います。
=== メンテナンス ===
==== 定期的なバックアップ ====
設定ファイルや重要なデータの定期的なバックアップを行います。以下のスクリプトは、<code>/usr/local/etc/apache24</code>ディレクトリのバックアップを作成する例です。
:<syntaxhighlight lang=csh>
#!/bin/sh
BACKUP_DIR="/backup/apache24"
DATE=$(date +"%Y%m%d")
tar -czf $BACKUP_DIR/apache24_$DATE.tar.gz /usr/local/etc/apache24
</syntaxhighlight>
このスクリプトをcronジョブに登録し、定期的に実行します。
:<syntaxhighlight lang=csh>
crontab -e
</syntaxhighlight>
以下の行を追加します(毎日午前2時にバックアップを実行)。
:<syntaxhighlight lang=text>
0 2 * * * /path/to/backup_script.sh
</syntaxhighlight>
==== アップデートとパッチの適用 ====
セキュリティホールやバグを修正するために、Apache HTTP Serverや関連パッケージを定期的にアップデートします。
:<syntaxhighlight lang=csh>
pkg update && pkg upgrade apache24
</syntaxhighlight>
==== ログのローテーションとクリーンアップ ====
ログファイルのサイズが大きくならないように、定期的にログローテーションを行い、古いログファイルをクリーンアップします。<code>newsyslog</code>の設定を使用して、自動的にログローテーションを行います。
==== サーバーヘルスチェック ====
サーバーのヘルスチェックを定期的に実施し、異常がないか確認します。例えば、以下のようなスクリプトをcronジョブに登録します。
:<syntaxhighlight lang=csh>
#!/bin/sh
if ! pgrep httpd > /dev/null
then
sudo service apache24 start
echo "Apache HTTP Server was restarted at $(date)" | mail -s "Apache HTTP Server restarted" admin@example.com
fi
</syntaxhighlight>
このスクリプトは、Apache HTTP Serverが動作していない場合に再起動し、管理者に通知します。
=== まとめ ===
この章では、Apache HTTP Serverのトラブルシューティングとメンテナンスについて説明しました。これらの手法を活用することで、サーバーの安定性と信頼性を向上させることができます。次章では、Apache HTTP Serverの拡張とモジュールについて詳しく説明します。
== 拡張とモジュール ==
Apache HTTP Serverは、多くのモジュールを利用することで機能を拡張できます。この章では、Apache HTTP Serverの拡張と代表的なモジュールの設定・利用方法について説明します。
=== モジュールの概要 ===
Apache HTTP Serverのモジュールは、大きく分けて以下の3つに分類されます。
; コアモジュール :基本的な機能を提供するモジュール。デフォルトで組み込まれています。
; 標準モジュール :一般的な拡張機能を提供するモジュール。必要に応じて有効化します。
; サードパーティモジュール :外部の開発者によって提供されるモジュール。追加インストールが必要です。
=== モジュールの有効化と無効化 ===
モジュールを有効化するには、<code>LoadModule</code>ディレクティブを使用します。無効化するには、この行をコメントアウトします。
:<syntaxhighlight lang=apache>
LoadModule rewrite_module libexec/apache24/mod_rewrite.so
LoadModule cgi_module libexec/apache24/mod_cgi.so
</syntaxhighlight>
=== 代表的なモジュールとその設定 ===
==== mod_rewrite ====
<code>mod_rewrite</code>は、URLの書き換えを行う強力なモジュールです。SEO対策やURLのリダイレクトに使用されます。
有効化するには、<code>httpd.conf</code>に以下の行を追加します。
:<syntaxhighlight lang=apache>
LoadModule rewrite_module libexec/apache24/mod_rewrite.so
</syntaxhighlight>
設定例:
:<syntaxhighlight lang=apache>
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^/old-page$ /new-page [R=301,L]
</IfModule>
</syntaxhighlight>
==== mod_ssl ====
<code>mod_ssl</code>は、SSL/TLSを使用して通信を暗号化するためのモジュールです。HTTPSをサポートするために使用します。
有効化するには、<code>httpd.conf</code>に以下の行を追加します。
:<syntaxhighlight lang=apache>
LoadModule ssl_module libexec/apache24/mod_ssl.so
Include etc/apache24/extra/httpd-ssl.conf
</syntaxhighlight>
SSL設定例(<code>httpd-ssl.conf</code>内):
:<syntaxhighlight lang=apache>
<VirtualHost *:443>
ServerName www.example.com
DocumentRoot "/usr/local/www/apache24/data"
SSLEngine on
SSLCertificateFile "/usr/local/etc/apache24/ssl/server.crt"
SSLCertificateKeyFile "/usr/local/etc/apache24/ssl/server.key"
<Directory "/usr/local/www/apache24/data">
Require all granted
</Directory>
</VirtualHost>
</syntaxhighlight>
==== mod_security ====
<code>mod_security</code>は、ウェブアプリケーションファイアウォール(WAF)として機能し、攻撃からサーバーを保護します。
インストールと有効化:
:<syntaxhighlight lang=csh>
pkg install mod_security
</syntaxhighlight>
<code>httpd.conf</code>に以下の行を追加します。
:<syntaxhighlight lang=apache>
LoadModule security2_module libexec/apache24/mod_security2.so
Include etc/modsecurity/modsecurity.conf
</syntaxhighlight>
設定例(<code>modsecurity.conf</code>内):
:<syntaxhighlight lang=apache>
<IfModule security2_module>
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecRule REQUEST_HEADERS:User-Agent "curl" "id:1001,phase:1,deny,status:403,msg:'Curl requests are not allowed'"
</IfModule>
</syntaxhighlight>
==== mod_proxy ====
<code>mod_proxy</code>は、リバースプロキシやロードバランシングを行うためのモジュールです。
有効化するには、<code>httpd.conf</code>に以下の行を追加します。
:<syntaxhighlight lang=apache>
LoadModule proxy_module libexec/apache24/mod_proxy.so
LoadModule proxy_http_module libexec/apache24/mod_proxy_http.so
</syntaxhighlight>
設定例:
:<syntaxhighlight lang=apache>
<IfModule mod_proxy.c>
ProxyPass "/app" "http://backend-server/app"
ProxyPassReverse "/app" "http://backend-server/app"
</IfModule>
</syntaxhighlight>
=== サードパーティモジュールの導入 ===
サードパーティモジュールは、公式リポジトリ外からインストールします。代表的なものとして、<code>mod_evasive</code>があります。これは、DDoS攻撃を防ぐためのモジュールです。
==== mod_evasiveのインストールと設定 ====
インストール手順:
:<syntaxhighlight lang=csh>
pkg install ap24-mod_evasive
</syntaxhighlight>
有効化と設定(<code>httpd.conf</code>内):
:<syntaxhighlight lang=apache>
LoadModule evasive20_module libexec/apache24/mod_evasive20.so
<IfModule mod_evasive20.c>
DOSHashTableSize 3097
DOSPageCount 2
DOSSiteCount 50
DOSPageInterval 1
DOSSiteInterval 1
DOSBlockingPeriod 10
</IfModule>
</syntaxhighlight>
=== モジュールのトラブルシューティング ===
モジュールの問題が発生した場合、以下の手順でトラブルシューティングを行います。
# '''エラーログの確認:''' <code>/var/log/httpd-error.log</code>を確認し、エラーメッセージを特定します。
# '''設定ファイルの確認:''' モジュールの設定ファイルに誤りがないか確認します。
# '''モジュールの依存関係:''' 必要な他のモジュールが正しくロードされているか確認します。
# '''バージョンの確認:''' モジュールのバージョンがApache HTTP Serverと互換性があるか確認します。
=== まとめ ===
この章では、Apache HTTP Serverの拡張と代表的なモジュールについて説明しました。これにより、サーバーの機能を拡張し、さまざまな要件に対応できるようになります。次章では、Apache HTTP Serverのセキュリティ強化について詳しく説明します。
== セキュリティ強化 ==
ウェブサーバーのセキュリティは非常に重要です。Apache HTTP Serverのセキュリティを強化することで、不正アクセスや攻撃からサーバーを保護できます。この章では、Apache HTTP Serverのセキュリティ強化のための具体的な設定やベストプラクティスについて説明します。
=== 基本的なセキュリティ設定 ===
==== サーバー情報の非公開化 ====
デフォルトの設定では、Apacheはエラーページにサーバー情報を表示します。この情報は攻撃者にとって有用な手がかりになるため、非公開にすることを推奨します。
:<syntaxhighlight lang=apache>
ServerSignature Off
ServerTokens Prod
</syntaxhighlight>
* <code>ServerSignature Off</code>: エラーページのサーバー署名を非表示にします。
* <code>ServerTokens Prod</code>: レスポンスヘッダーに最低限のサーバー情報のみを表示します。
==== ドキュメントルートの制限 ====
ドキュメントルートディレクトリへのアクセスを制限し、必要最低限のパーミッションのみを付与します。
:<syntaxhighlight lang=apache>
<Directory "/usr/local/www/apache24/data">
Options None
AllowOverride None
Require all granted
</Directory>
</syntaxhighlight>
* <code>Options None</code>: CGIやシンボリックリンクなどのオプションを無効化します。
* <code>AllowOverride None</code>: <code>.htaccess</code>ファイルによる設定の上書きを禁止します。
==== ファイルのパーミッション設定 ====
Apacheが読み書きできるファイルやディレクトリのパーミッションを適切に設定します。
:<syntaxhighlight lang=csh>
chmod -R 755 /usr/local/www/apache24/data
chown -R www:www /usr/local/www/apache24/data
</syntaxhighlight>
* <code>chmod -R 755</code>: 所有者に読み書き実行権限を付与し、グループと他のユーザーには読み実行権限のみを付与します。
* <code>chown -R www:www</code>: Apacheの実行ユーザーとグループに所有権を変更します。
=== HTTPSの導入 ===
HTTPSは、通信を暗号化し、データの盗聴や改ざんを防ぎます。前章で説明した<code>mod_ssl</code>を使用して設定します。
==== Let's Encryptの導入 ====
Let's Encryptは、無料のSSL/TLS証明書を提供するサービスです。<code>certbot</code>を使用して簡単に証明書を取得できます。
:<syntaxhighlight lang=csh>
pkg install py37-certbot
certbot certonly --webroot -w /usr/local/www/apache24/data -d example.com -d www.example.com
</syntaxhighlight>
証明書の取得後、<code>httpd-ssl.conf</code>を以下のように編集します。
:<syntaxhighlight lang=apache>
<VirtualHost *:443>
ServerName www.example.com
DocumentRoot "/usr/local/www/apache24/data"
SSLEngine on
SSLCertificateFile "/usr/local/etc/letsencrypt/live/example.com/fullchain.pem"
SSLCertificateKeyFile "/usr/local/etc/letsencrypt/live/example.com/privkey.pem"
<Directory "/usr/local/www/apache24/data">
Require all granted
</Directory>
</VirtualHost>
</syntaxhighlight>
==== HTTPからHTTPSへのリダイレクト ====
HTTPでのアクセスを自動的にHTTPSへリダイレクトします。<code>httpd.conf</code>に以下の設定を追加します。
:<syntaxhighlight lang=apache>
<VirtualHost *:80>
ServerName www.example.com
Redirect permanent / https://www.example.com/
</VirtualHost>
</syntaxhighlight>
=== セキュリティモジュールの利用 ===
==== mod_security ====
前章で説明した<code>mod_security</code>は、ウェブアプリケーションファイアウォールとして機能し、攻撃を検出・防御します。
:<syntaxhighlight lang=apache>
<IfModule security2_module>
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecRule REQUEST_HEADERS:User-Agent "curl" "id:1001,phase:1,deny,status:403,msg:'Curl requests are not allowed'"
</IfModule>
</syntaxhighlight>
==== mod_evasive ====
<code>mod_evasive</code>は、DDoS攻撃やブルートフォース攻撃を防ぐためのモジュールです。
:<syntaxhighlight lang=apache>
<IfModule mod_evasive20.c>
DOSHashTableSize 3097
DOSPageCount 2
DOSSiteCount 50
DOSPageInterval 1
DOSSiteInterval 1
DOSBlockingPeriod 10
</IfModule>
</syntaxhighlight>
=== その他のセキュリティ対策 ===
==== バージョンの定期的な更新 ====
Apache HTTP Serverのバージョンを最新のものに保つことで、既知の脆弱性を防ぎます。
:<syntaxhighlight lang=csh>
pkg update
pkg upgrade apache24
</syntaxhighlight>
==== 不要なモジュールの無効化 ====
使用しないモジュールは無効にすることで、攻撃のリスクを減少させます。<code>httpd.conf</code>から該当モジュールの<code>LoadModule</code>行をコメントアウトします。
:<syntaxhighlight lang=apache>
LoadModule status_module libexec/apache24/mod_status.so
LoadModule cgi_module libexec/apache24/mod_cgi.so
</syntaxhighlight>
==== ファイアウォールの設定 ====
FreeBSDのファイアウォール(pfやipfw)を使用して、不要なポートへのアクセスを制限します。
例として、<code>pf</code>を使用した設定ファイル(<code>/etc/pf.conf</code>)を以下に示します。
:<syntaxhighlight lang=csh>
ext_if="vtnet0"
set skip on lo
block in all
pass out all keep state
pass in on $ext_if proto tcp from any to any port 80
pass in on $ext_if proto tcp from any to any port 443
</syntaxhighlight>
ファイアウォールを有効にします。
:<syntaxhighlight lang=csh>
service pf start
pfctl -f /etc/pf.conf
</syntaxhighlight>
=== まとめ ===
この章では、Apache HTTP Serverのセキュリティ強化のための設定とベストプラクティスについて説明しました。これらの対策を実施することで、サーバーの安全性を大幅に向上させることができます。次章では、Apache HTTP Serverのパフォーマンスチューニングについて詳しく説明します。
== 実践例とベストプラクティス ==
この章では、実際の設定例とベストプラクティスを通じて、Apache HTTP Serverの運用をより効率的に行うための手法を紹介します。特に、よくある設定例や特定のアプリケーションのホスティングについて詳述します。
=== よくある設定例 ===
==== 静的サイトのホスティング ====
Apache HTTP Serverを使用して静的コンテンツをホストする場合の設定例です。
:<syntaxhighlight lang=apache>
<VirtualHost *:80>
ServerName www.example.com
DocumentRoot "/usr/local/www/apache24/data"
<Directory "/usr/local/www/apache24/data">
Require all granted
Options Indexes FollowSymLinks
AllowOverride None
</Directory>
ErrorLog "/var/log/httpd/example.com-error_log"
CustomLog "/var/log/httpd/example.com-access_log" common
</VirtualHost>
</syntaxhighlight>
==== PHPアプリケーションのホスティング ====
PHPを使用した動的なウェブサイトをホストする設定例です。
:<syntaxhighlight lang=apache>
<VirtualHost *:80>
ServerName www.example.com
DocumentRoot "/usr/local/www/apache24/data"
<Directory "/usr/local/www/apache24/data">
Require all granted
Options Indexes FollowSymLinks MultiViews
AllowOverride All
DirectoryIndex index.php
</Directory>
ErrorLog "/var/log/httpd/example.com-error_log"
CustomLog "/var/log/httpd/example.com-access_log" common
</VirtualHost>
</syntaxhighlight>
=== WordPressのホスティング ===
WordPressをホストするための設定例です。WordPressはPHPとMySQLを使用するため、適切なモジュールの有効化と設定が必要です。
:<syntaxhighlight lang=apache>
<VirtualHost *:80>
ServerName www.example.com
DocumentRoot "/usr/local/www/apache24/data"
<Directory "/usr/local/www/apache24/data">
Require all granted
Options Indexes FollowSymLinks MultiViews
AllowOverride All
DirectoryIndex index.php
</Directory>
ErrorLog "/var/log/httpd/example.com-error_log"
CustomLog "/var/log/httpd/example.com-access_log" common
# Enable PHP
<FilesMatch \.php$>
SetHandler application/x-httpd-php
</FilesMatch>
# WordPress Rewrite Rules
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
</VirtualHost>
</syntaxhighlight>
=== リバースプロキシの設定 ===
Apache HTTP Serverをリバースプロキシとして使用する設定例です。
:<syntaxhighlight lang=apache>
<VirtualHost *:80>
ServerName www.example.com
ProxyPreserveHost On
ProxyPass / http://backend-server/
ProxyPassReverse / http://backend-server/
ErrorLog "/var/log/httpd/example.com-error_log"
CustomLog "/var/log/httpd/example.com-access_log" common
</VirtualHost>
</syntaxhighlight>
=== 運用上のベストプラクティス ===
==== バックアップとリストア ====
定期的なバックアップを作成し、必要な場合に迅速にリストアできるようにします。
==== 定期メンテナンス ====
定期的なパッケージのアップデートとApache HTTP Serverのバージョン管理を行い、セキュリティを維持します。
==== セキュリティの確保 ====
前章で述べたセキュリティ設定を適用し、ファイアウォールなどの追加のセキュリティ対策を検討します。
=== まとめ ===
この章では、実際の設定例と運用上のベストプラクティスについて紹介しました。これらの手法を活用することで、Apache HTTP Serverを安全に効率的に運用し、特定のアプリケーションや使用シナリオに最適化することができます。
== 附録 ==
この附録では、Apache HTTP Serverに関連する参考文献、オンラインリソース、コマンドリファレンス、FAQ、用語集などを提供します。これらの情報は、Apache HTTP Serverの理解を深めるための補足資料として役立ちます。
=== 参考文献とリソース ===
{{Wikipedia}}
Apache HTTP Serverに関するさまざまな参考文献やオンラインリソースがあります。以下はいくつかの推奨リソースです。
* Apache Software Foundation 公式サイト: [https://httpd.apache.org/ https://httpd.apache.org/]
* Apache HTTP Server Documentation: [https://httpd.apache.org/docs/ https://httpd.apache.org/docs/]
* FreeBSD Handbook - Apache: [https://www.freebsd.org/doc/handbook/network-apache.html https://www.freebsd.org/doc/handbook/network-apache.html]
=== オンラインドキュメント ===
Apache HTTP Serverの詳細なドキュメントは、公式サイトの以下のセクションで提供されています。
* Apache HTTP Server Documentation: [https://httpd.apache.org/docs/ https://httpd.apache.org/docs/]
** 最新のモジュールや設定オプションに関する詳細な情報が含まれています。
=== コミュニティフォーラム ===
Apache HTTP Serverに関する質問や討論を行うためのコミュニティフォーラムもあります。
* Apache HTTP Server Community Support: [https://community.apache.org/support.html https://community.apache.org/support.html]
** ユーザーコミュニティによるサポートや情報交換が行われています。
=== コマンドリファレンス ===
Apache HTTP Serverのコマンドリファレンスを参照することで、特定のコマンドや設定オプションの意味や使用方法を確認できます。
* Apache HTTP Server Command Reference: [https://httpd.apache.org/docs/current/programs/index.html https://httpd.apache.org/docs/current/programs/index.html]
=== よく使うApacheコマンド一覧 ===
Apache HTTP Serverを管理する際によく使用されるコマンドの一覧です。
* <code>apachectl</code>: Apache HTTP Serverの制御スクリプト
* <code>httpd</code>: Apache HTTP Serverのメインバイナリ
* <code>htpasswd</code>: パスワードファイルを生成および管理するためのユーティリティ
* <code>htdigest</code>: ダイジェスト認証のためのユーティリティ
[[Category:ウェブサーバー]]
sqwa099h19c3b8o212r2oo9sneq3mbj
プログラミング/ドメイン駆動設計
0
43506
264522
2024-11-29T16:38:47Z
Ef3
694
ドメイン駆動設計(Domain-Driven Design、DDD)は、複雑なソフトウェアシステムを効果的に構築するための設計手法の一つです。このアプローチでは、ソフトウェアが解決すべき問題領域、すなわち「ドメイン」に強く焦点を当てます。DDDの目的は、ドメインの専門家と開発者が深い理解を共有し、これをもとにソフトウェアのモデルを作り上げることです。
264522
wikitext
text/x-wiki
== ドメイン駆動設計 ==
'''ドメイン駆動設計'''(Domain-Driven Design、DDD)は、複雑なソフトウェアシステムを効果的に構築するための設計手法の一つです。このアプローチでは、ソフトウェアが解決すべき問題領域、すなわち「ドメイン」に強く焦点を当てます。DDDの目的は、ドメインの専門家と開発者が深い理解を共有し、これをもとにソフトウェアのモデルを作り上げることです。
=== コアコンセプト ===
DDDの中核となる考え方は、ドメインモデルを中心にソフトウェア設計を進めることです。このドメインモデルは、開発チームとドメインの専門家が協力して作成し、問題の本質を捉える抽象化された表現です。ドメインモデルは単なるデータ構造ではなく、ドメインのルールや振る舞いを表現したものです。
また、DDDでは、'''境界づけられたコンテキスト'''(Bounded Context)の概念も重要です。これは、システムを適切なスコープで区切り、それぞれの領域で独立したモデルを管理することを意味します。これにより、大規模なシステムでも複雑さを制御しやすくなります。
=== 実践の流れ ===
DDDを実践する際の典型的な流れとしては、まずドメインの理解を深めることから始まります。チームはドメインの専門家と密接に協力し、'''ユビキタス言語'''(Ubiquitous Language)と呼ばれる共通の用語を定義します。このユビキタス言語は、モデル設計、コード、ドキュメントなど、あらゆる場面で一貫して使用されます。
次に、モデルを設計し、ソフトウェアのアーキテクチャに反映させます。この際、エンティティや値オブジェクト、アグリゲート、リポジトリ、サービスなどの設計要素を活用します。これらの要素は、それぞれが明確な役割を持ち、モデルの振る舞いを正確に表現します。
=== DDDの利点 ===
DDDを採用することで、開発チームはドメインに特化した深い知識を得ることができ、結果として問題解決に最適化されたソフトウェアを設計することが可能になります。また、ユビキタス言語を用いることで、専門家と開発者間の意思疎通がスムーズになり、誤解や手戻りを減らすことができます。
さらに、境界づけられたコンテキストを導入することで、大規模システムの設計や開発が効率的になり、変更に強い柔軟なアーキテクチャを構築することが可能です。
=== 課題と注意点 ===
一方で、DDDの導入にはいくつかの課題も存在します。ドメインの理解には時間がかかり、専門家と開発者の協力が欠かせません。また、適切なモデルを設計するには高い抽象化能力が必要です。さらに、境界づけられたコンテキストを過剰に分割すると、システム全体の統合が複雑になるリスクもあります。
=== まとめ ===
ドメイン駆動設計は、ソフトウェア開発の複雑性を管理し、ドメインに特化した価値あるシステムを構築するための強力なアプローチです。成功させるためには、専門家と開発者が協力し、ドメインモデルを中心に設計を進めることが重要です。また、設計上の決定を慎重に行い、システム全体のバランスを保つことも忘れてはなりません。
{{DEFAULTSORT:とめいんくとうせつけい}}
[[Category:プログラミング]]
42l7qc1aklxwncp5x578r7n3kdijefz
トーク:高等学校政治経済/大日本帝国憲法と日本国憲法
1
43507
264523
2024-11-29T16:45:55Z
しゃちのアカウント
46198
/* 教科書記述の妥当性について */ 新しい節
264523
wikitext
text/x-wiki
== 教科書記述の妥当性について ==
①事実(とされるもの)を書く
「なお、よく言われる「天皇の権限は非常に強かった」というのは誤謬である。」「「大日本帝国憲法は遅れていた」というのも重大な誤解である」などの記述は、教科書に載っている記述ではありませんし、なんなら指導書にも書いてありません。そして、憲法学の通説でもありません。Wikibooksは自説を書く場所ではありません。
②学習指導要領との関係
現行学習指導要領解説では、「日本国憲法が保障している基本的人権を取り上げ、その内容、確立の歴史的背景・経緯、政治制度との関連などについて考察することを通して、個人の尊厳、自由、平等などの社会的価値について理解を深めることができるようにする。その際、権利相互の関係や人権をめぐる諸課題についても理解できるようにする。」とあります。すなわち、現在の憲法によって基本的人権が規定されていることを目的として、伝える必要があります(当たり前ですが、教科書記述もそれにのっとっており、例えば、東京書籍版の該当節は、「日本国憲法の基本原理」となっています)。明治憲法が素晴らしかったことを伝えることが目的の節ではありません。
③コラムについて
これは福田恆存が提起したものでしょうが、わざわざこのコラムを載せる意図が分かりかねます。--[[利用者:しゃちのアカウント|しゃちのアカウント]] ([[利用者・トーク:しゃちのアカウント|トーク]]) 2024年11月29日 (金) 16:45 (UTC)
31g6oh78gkc0tfvjtji854rtjyysgrl
264537
264523
2024-11-30T02:52:00Z
~2024-20571
85135
/* 教科書記述の妥当性について */ 返信
264537
wikitext
text/x-wiki
== 教科書記述の妥当性について ==
①事実(とされるもの)を書く
「なお、よく言われる「天皇の権限は非常に強かった」というのは誤謬である。」「「大日本帝国憲法は遅れていた」というのも重大な誤解である」などの記述は、教科書に載っている記述ではありませんし、なんなら指導書にも書いてありません。そして、憲法学の通説でもありません。Wikibooksは自説を書く場所ではありません。
②学習指導要領との関係
現行学習指導要領解説では、「日本国憲法が保障している基本的人権を取り上げ、その内容、確立の歴史的背景・経緯、政治制度との関連などについて考察することを通して、個人の尊厳、自由、平等などの社会的価値について理解を深めることができるようにする。その際、権利相互の関係や人権をめぐる諸課題についても理解できるようにする。」とあります。すなわち、現在の憲法によって基本的人権が規定されていることを目的として、伝える必要があります(当たり前ですが、教科書記述もそれにのっとっており、例えば、東京書籍版の該当節は、「日本国憲法の基本原理」となっています)。明治憲法が素晴らしかったことを伝えることが目的の節ではありません。
③コラムについて
これは福田恆存が提起したものでしょうが、わざわざこのコラムを載せる意図が分かりかねます。--[[利用者:しゃちのアカウント|しゃちのアカウント]] ([[利用者・トーク:しゃちのアカウント|トーク]]) 2024年11月29日 (金) 16:45 (UTC)
:①>この記述に関しては、『大日本帝国憲法を無条件で「悪」と断じる風潮』が一般に存在するために追加しました。[https://www.jicl.jp/articles/opinion_20240521.html こちら]も参照してください。また、必ずしも学習指導要領に合わせる必要はないと思います。ゆとり時代のような歯止め規定が存在するわけでもありませんし、文科省は「'''学習状況などその実態等に応じて必要がある場合には,各学校の判断により,学習指導要領に示していない内容を加えて指導することも可能'''」と述べています(実際、WBの高校数学・高校化学では学習指導要領の内容を超えた内容も扱っている)。
:②>別に明治憲法が素晴らしかったことを伝えることが目的ではありません。「明治憲法は当時の視点から見れば(欠陥はあったものの)評価に値するものであった(実際、当時の英国の社会科学者ハーバート・スペンサーも評価した)」「明治憲法を悪いものとみなすのはあくまでも現代からの視点である」という観点を伝え、偏った視点からではなく多角的な視点(この場合、『明治憲法は(現代から見て)遅れている』という視点と『明治憲法は(当時から見て)評価に値する』という視点)から(日本史・世界史にも絡めて)両憲法を見つめ直す良い機会になれば、と追加しました。
:③>日本国憲法を学習している際に、『文章に違和感を覚える』『何か日本語が変』という感想を抱く学生は(主観だが)かなりいます(特に国語を得意とする学生)。そのような学生に憲法の文法的誤謬を示すことで、「そのような感想を抱いた理由へのモヤモヤ」を解消することを目的としています。また、「文法エラーの修正を目的とした改憲論」も存在するという事実を示すことで、学生が改憲論に関する偏った認識(改憲論者=戦争愛好者、改憲論者=日本の破壊者 etc.)に陥るのを防ぐ目的もあります。--[[特別:投稿記録/~2024-20571|~2024-20571]] ([[利用者・トーク:~2024-20571|会話]]) 2024年11月30日 (土) 02:52 (UTC)
hcsluxyyh8qj8ucqh21153kyc27lha1
プログラミング/例外処理
0
43508
264539
2024-11-30T04:13:38Z
Ef3
694
/*例外とは何か*/ 例外(エクセプション)とは、プログラムの実行中に発生する予期せぬ異常や、通常の処理の流れを中断させる事象のことを指します。これは、ファイルの読み書きエラー、ネットワーク接続の問題、不正な入力値の処理など、さまざまな状況で発生します。
264539
wikitext
text/x-wiki
== 例外処理の基本概念 ==
=== 例外とは何か ===
例外(エクセプション)とは、プログラムの実行中に発生する予期せぬ異常や、通常の処理の流れを中断させる事象のことを指します。これは、ファイルの読み書きエラー、ネットワーク接続の問題、不正な入力値の処理など、さまざまな状況で発生します。
=== 例外処理の重要性 ===
例外処理は、以下の理由から非常に重要です:
* プログラムの安定性を確保する
* エラー発生時の適切な対応を可能にする
* システムの堅牢性を向上させる
* ユーザーに有益なエラー情報を提供する
== 基本的な例外処理の構文 ==
=== try-catch-finally ブロック ===
基本的な例外処理の構文は以下のように構成されます:
:<syntaxhighlight lang=java copy>
try {
// 例外が発生する可能性のあるコード
} catch (特定の例外クラス 例外変数) {
// 例外発生時の処理
} finally {
// 例外の有無に関わらず常に実行されるコード
}
</syntaxhighlight>
=== 例外のキャッチと処理 ===
例外は、発生する可能性のあるコードを '''try''' ブロックで囲み、対応する '''catch''' ブロックで処理します。'''finally''' ブロックは、例外の有無に関わらず必ず実行されます。
== 主な例外の種類 ==
=== 例外の分類 ===
例外は大きく2つに分類されます:
* チェック例外(Checked Exception): コンパイル時にチェックされる例外
* 非チェック例外(Unchecked Exception): 実行時にのみ発生する例外
=== 代表的な例外 ===
* '''IOException''': ファイルや入出力に関連する例外
* '''NullPointerException''': null参照に対する操作
* '''ArrayIndexOutOfBoundsException''': 配列の範囲外アクセス
* '''ArithmeticException''': 算術的な例外(ゼロ除算など)
== 例外のスロー ==
=== throw キーワード ===
'''throw''' キーワードを使用して、明示的に例外をスローできます:
:<syntaxhighlight lang=java copy>
if (age < 0) {
throw new IllegalArgumentException("年齢は負の値にできません");
}
</syntaxhighlight>
=== 独自の例外クラス作成 ===
独自の例外クラスを作成することで、より具体的で意味のある例外処理が可能になります:
:<syntaxhighlight lang=java copy>
public class CustomValidationException extends Exception {
public CustomValidationException(String message) {
super(message);
}
}
</syntaxhighlight>
== 例外処理のベストプラクティス ==
=== 適切な例外処理 ===
* 具体的な例外をキャッチする
* 必要以上に広範囲な例外のキャッチを避ける
* 意味のあるエラーメッセージを提供する
=== 例外のログ記録 ===
例外は、デバッグと追跡のためにログに記録することが重要です:
:<syntaxhighlight lang=java copy>
try {
// 処理
} catch (SomeException e) {
logger.error("エラーが発生しました", e);
}
</syntaxhighlight>
== リソース管理と自動クローズ ==
=== try-with-resources ===
リソースの自動クローズを行う '''try-with-resources''' 構文:
:<syntaxhighlight lang=java copy>
try (FileReader reader = new FileReader("example.txt")) {
// ファイル処理
} catch (IOException e) {
// エラー処理
}
// リソースは自動的にクローズされる
</syntaxhighlight>
== 演習問題 ==
=== 基本的な例外処理の実装 ===
ユーザー入力の年齢に対して、不正な値(負の数)が入力された場合の例外処理を実装しなさい。
=== カスタム例外の作成 ===
銀行口座の残高管理において、残高不足時にスローされるカスタム例外クラスを作成しなさい。
=== リソース管理 ===
ファイルの読み書きを行うメソッドを作成し、適切な例外処理とリソース管理を実装しなさい。
=== まとめ ===
例外処理は、堅牢で信頼性の高いプログラムを作成するための重要な概念です。適切な例外処理により、プログラムの安定性と保守性を大幅に向上させることができます。
{{DEFAULTSORT:れいかいしより}}
[[Category:プログラミング]]
96sf0zu5fm5iiwvz682ftmre8k9fv3x
プログラミング/エラーハンドリング
0
43509
264541
2024-11-30T04:31:58Z
Ef3
694
/*エラーハンドリングの定義*/ エラーハンドリングとは、プログラム実行中に発生する予期せぬ、または異常な状況を検出、管理、対応するための技術的アプローチです。これは、プログラムの堅牢性と信頼性を確保するための重要な戦略です。
264541
wikitext
text/x-wiki
== エラーハンドリングの基本概念 ==
=== エラーハンドリングの定義 ===
エラーハンドリングとは、プログラム実行中に発生する予期せぬ、または異常な状況を検出、管理、対応するための技術的アプローチです。これは、プログラムの堅牢性と信頼性を確保するための重要な戦略です。
=== 例外処理との違い ===
* エラーハンドリングは、主にエラー状態の検出と管理に焦点を当てる
* 例外処理は、異常な実行フローの割り込みと処理に重点を置く
* エラーハンドリングは、より広範囲なエラー管理戦略を含む
== エラー検出と対応の基本パターン ==
=== エラー検出の方法 ===
* 戻り値によるエラー通知
* エラーコードの利用
* ブール値による成功/失敗の表現
* エラーオブジェクトの返却
=== エラーコードによるハンドリング ===
典型的なC言語スタイルのエラーハンドリング:
:<syntaxhighlight lang=c copy>
int readFile(char* filename) {
FILE* file = fopen(filename, "r");
if (file == NULL) {
return ERROR_FILE_NOT_FOUND;
}
// ファイル処理
return SUCCESS;
}
</syntaxhighlight>
C言語におけるエラーハンドリングは、戻り値を用いた最も原始的かつ効率的なエラー通知メカニズムを提供します。この方法では、関数の実行結果を整数値として返し、呼び出し側が明示的にエラーコードを確認することで、異常状態を検出します。
上記のコード例では、ファイルオープンの成否を戻り値で通知しています。NULLチェックにより、ファイルが開けない場合に特定のエラーコードを返し、呼び出し元に処理の失敗を伝達します。この手法は、関数呼び出しのオーバーヘッドが低く、リソース制約の厳しい環境でも効果的に機能します。
== 言語別のエラーハンドリング技法 ==
=== 関数型言語のアプローチ ===
==== Rust言語のResult型 ====
:<syntaxhighlight lang=rust copy>
fn divide(a: f64, b: f64) -> Result<f64, String> {
if a == 0.0 && b == 0.0 {
Err("ゼロゼロ除算エラー".to_string())
} else if b == 0.0 {
Err("ゼロ除算エラー".to_string())
} else {
Ok(a / b)
}
}
</syntaxhighlight>
関数型プログラミング言語、特にRustは、型システムを活用した革新的なエラーハンドリング手法を提供します。Result型は、成功と失敗の両方のケースを明示的に表現可能な代数的データ型で、エラーを第一級の値として扱います。
このdivide関数は、除算の複雑な条件(ゼロ除算)を型安全な方法で処理します。成功時はOk、エラー時はErrを返すことで、呼び出し側に明確な選択肢を提供し、エラーケースの網羅的な処理を強制します。型システムがエラーハンドリングの漏れを静的に検出するため、実行時エラーを大幅に削減でき
==== Swiftのオプショナル型 ====
:<syntaxhighlight lang=swift copy>
var optionalValue: Int? = nil
let result = optionalValue ?? 0 // Null合体演算子
</syntaxhighlight>
=== モダンな言語のエラーハンドリング ===
==== Kotlinのnull安全性 ====
:<syntaxhighlight lang=kotlin copy>
fun processValue(value: String?) {
val length = value?.length ?: 0 // Null合体演算子
}
</syntaxhighlight>
現代のプログラミング言語は、null参照によるエラーを言語レベルで防止する機能を提供しています。Kotlinの例では、null許容型(?)とセーフコール演算子(?.)、null合体演算子(?:)を組み合わせて、安全で簡潔なnullチェックを実現しています。
このコードは、nullの可能性がある文字列の長さを安全に取得する方法を示しています。value?.lengthはvalueがnullの場合にnullを返し、?: 0により、nullの場合にデフォルト値0を設定します。これにより、明示的なnullチェックのボイラープレートコードを排除し、コードの可読性と安全性を向上させます。
==== JavaScriptのnull条件演算子 ====
:<syntaxhighlight lang=javascript copy>
const userName = user?.name ?? 'ゲスト'; // null/undefinedの場合デフォルト値
</syntaxhighlight>
== エラーハンドリングのデザインパターン ==
=== センチネル値パターン ===
特定の値をエラー状態のマーカーとして使用:
:<syntaxhighlight lang=python3 copy>
def find_item(items, target):
ITEM_NOT_FOUND = object() # センチネル値
for item in items:
if item == target:
return item
return ITEM_NOT_FOUND
</syntaxhighlight>
センチネル値パターンは、エラーや特殊な状態を表現するためのシンプルで柔軟な手法です。Pythonの例では、ユニークなオブジェクトを使用して、アイテムが見つからない状態を表現しています。
従来の例外やエラーコードとは異なり、このアプローチは軽量で、特定のユースケースに対して直接的な解決策を提供します。オブジェクト()は常にユニークなインスタンスを生成するため、確実に識別可能な「見つからない」状態を作り出せます。これは、パフォーマンスが重要で、重量級のエラーハンドリングが過剰な場合に特に有効です。
=== 関数型エラーハンドリング ===
エラーを第一級オブジェクトとして扱う:
:<syntaxhighlight lang=haskell copy>
data Result a = Success a | Failure Error
validateUser :: User -> Result User
validateUser user =
if isValidName user
then Success user
else Failure InvalidNameError
</syntaxhighlight>
関数型プログラミングのエラーハンドリングは、エラーを通常のデータと同等に扱う、根本的に異なるアプローチを採用しています。Haskellの例では、Result型を使用して、成功と失敗の両方のケースを明示的かつ型安全に表現しています。
このvalidateUser関数は、ユーザーの妥当性検証を行い、成功時はユーザーオブジェクト、失敗時は具体的なエラーを返します。パターンマッチングと組み合わせることで、エラーケースを網羅的かつ明示的に処理でき、予期せぬエラー状態を防ぎます。これは、単なる例外処理を超えた、より宣言的で安全なエラーハンドリングの形態を示しています。
== エラーロギングと診断 ==
=== エラーログの設計 ===
効果的なエラーログには以下の要素が重要:
* タイムスタンプ
* エラーレベル(INFO, WARNING, ERROR)
* エラーメッセージ
* スタックトレース
* コンテキスト情報
=== ログレベルの例 ===
:<syntaxhighlight lang=python3 copy>
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
try:
# 危険な操作
result = risky_operation()
except Exception as e:
logger.error(f"エラーが発生: {e}", exc_info=True)
</syntaxhighlight>
== エラーハンドリングのベストプラクティス ==
=== エラー処理の原則 ===
* 早期検出
* 明確なエラーメッセージ
* 適切な回復または安全な終了
* 重要な状態の保護
== 演習問題 ==
=== エラーハンドリングの実装 ===
# Result型を使用して、安全な除算関数を実装しなさい。
# null条件演算子を用いて、ユーザー情報の安全な取得を行いなさい。
=== まとめ ===
各プログラミングパラダイムと言語は、エラーハンドリングに対して独自のアプローチを提供します。これらの手法は、コードの安全性、可読性、保守性を向上させる重要な戦略であり、プログラマーは言語の特性と問題の文脈に応じて、最適な方法を選択する必要があります。
{{DEFAULTSORT:えらはんとりんく}}
[[Category:プログラミング]]
a6hzfpnecjmkcy60sxwcw9yhjcco46k
プログラミング/割り込み
0
43510
264542
2024-11-30T04:44:46Z
Ef3
694
/*割り込みの基本概念*/ 割り込みは、コンピュータシステムにおける重要な制御メカニズムです。通常のプログラム実行の流れを一時的に中断し、特定のイベントや緊急の処理を即座に対応するための仕組みです。ハードウェアやソフトウェアからのシグナルに応じて、現在実行中のタスクを中断し、別の処理に切り替えることができます。
264542
wikitext
text/x-wiki
== 割り込みの基本概念 ==
割り込みは、コンピュータシステムにおける重要な制御メカニズムです。通常のプログラム実行の流れを一時的に中断し、特定のイベントや緊急の処理を即座に対応するための仕組みです。ハードウェアやソフトウェアからのシグナルに応じて、現在実行中のタスクを中断し、別の処理に切り替えることができます。
=== 割り込みハンドラの基本 ===
割り込みハンドラは、特定の割り込みイベントに対応するための特別な関数です。以下のコード例は、タイマー割り込みを処理する基本的な方法を示しています:
:<syntaxhighlight lang=c copy>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
int flag = 0;
void timer_interrupt_handler(int signum) {
// タイマー割り込み時に実行される処理
printf("タイマー割り込みが発生しました!\n");
flag = 1;
}
int main() {
// SIGALRMシグナルのハンドラを設定
signal(SIGALRM, timer_interrupt_handler);
// 3秒後にアラーム(割り込み)を発生
alarm(3);
// メインプログラムの処理
for (;;) {
printf("メインプログラムの実行中...\n");
sleep(1);
if (flag != 0) {
break;
}
}
return 0;
}
</syntaxhighlight>
== 割り込みの種類 ==
割り込みは主に以下のカテゴリに分類されます:
=== ハードウェア割り込み ===
ハードウェア割り込みは、物理デバイス(キーボード、マウス、ディスクなど)からのシグナルによって発生します。例えば、キーボード入力や外部デバイスの状態変化が典型的な例です。
=== ソフトウェア割り込み ===
ソフトウェア割り込みは、プログラム内部で明示的に生成される割り込みです。システムコールの呼び出しや例外処理がこれに該当します。
=== 例外割り込み ===
例外割り込みは、プログラム実行中に発生する異常な状況(ゼロ除算、メモリアクセス違反など)に対応するための割り込みです。
==== 例外処理の具体例 ====
以下のコード例は、ゼロ除算例外を処理する方法を示しています:
:<syntaxhighlight lang=c copy>
#include <signal.h>
#include <stdio.h>
#include <setjmp.h>
jmp_buf exception_env;
void division_by_zero_handler(int signum) {
printf("ゼロ除算エラーを検出しました!\n");
longjmp(exception_env, 1);
}
int safe_division(int numerator, int denominator) {
// ゼロ除算の前にハンドラを設定
signal(SIGFPE, division_by_zero_handler);
// setjmpで例外発生時のジャンプポイントを設定
if (setjmp(exception_env) == 0) {
return numerator / denominator;
} else {
// 例外発生時の処理
return 0;
}
}
int main() {
int result = safe_division(10, 0);
printf("除算結果: %d\n", result);
return 0;
}
</syntaxhighlight>
== 割り込み管理の重要な概念 ==
=== 割り込み優先度 ===
異なる割り込みイベント間には優先度が存在します。高優先度の割り込みは、低優先度の割り込みよりも優先的に処理されます。
=== コンテキストスイッチ ===
割り込み発生時、現在のプログラムの実行状態(レジスタ、プログラムカウンタなど)を保存し、割り込みハンドラの処理後に元の状態に復帰する仕組みをコンテキストスイッチと呼びます。
=== 割り込みの無効化と再有効化 ===
システムの安定性を確保するため、重要な処理中には割り込みを一時的に無効化し、処理完了後に再度有効化することがあります。
==== 割り込み無効化の例 ====
:<syntaxhighlight lang=c copy>
#include <signal.h>
#include <stdio.h>
void critical_section() {
sigset_t mask;
// 全ての割り込みをブロック
sigfillset(&mask);
sigprocmask(SIG_BLOCK, &mask, NULL);
// 重要な処理
printf("クリティカルセクション:割り込み無効\n");
// 割り込みを再有効化
sigprocmask(SIG_UNBLOCK, &mask, NULL);
}
int main() {
critical_section();
return 0;
}
</syntaxhighlight>
== まとめ ==
割り込みは、現代のコンピュータシステムにおいて不可欠な機能です。効果的な割り込み管理は、リアルタイムシステム、組み込みシステム、オペレーティングシステムの設計において極めて重要な概念となります。
{{DEFAULTSORT:わりこみ}}
[[Category:プログラミング]]
prqy1sakgl31op3m0zawbxsuldm24r4
プログラミング/型消去
0
43511
264545
2024-11-30T05:37:33Z
Ef3
694
型消去(Type Erasure)とは、プログラムの実行時に、特定の型情報を隠蔽し、代わりに動的に型を決定する技術のことを指します。この技法は、特に静的型付け言語において、動的ポリモーフィズムを実現するために使用されます。型消去は、プログラムの柔軟性を高め、異なる型を共通のインターフェースを通じて扱うことを可能にします。
264545
wikitext
text/x-wiki
== 型消去 (Type Erasure) ==
型消去(Type Erasure)とは、プログラムの実行時に、特定の型情報を隠蔽し、代わりに動的に型を決定する技術のことを指します。この技法は、特に静的型付け言語において、動的ポリモーフィズムを実現するために使用されます。型消去は、プログラムの柔軟性を高め、異なる型を共通のインターフェースを通じて扱うことを可能にします。
=== 型消去の基本概念 ===
型消去は、コンパイル時に型情報を消去または隠蔽し、実行時に型情報を決定することによって、静的型付けの強い言語でも動的な型システムを実現します。この技法により、異なる型を統一的に扱うことができます。
型消去の典型的な使い方としては、以下のケースがあります:
* トレイトやインターフェースを通じて、異なる型を共通の型にまとめる。
* 動的に型を解決することで、柔軟で拡張性の高いコードを書くことができる。
=== 型消去の実装例 ===
==== Rustの<code>dyn</code>トレイト ====
Rustでは、<code>dyn</code>キーワードを使ってトレイトオブジェクトを作成することができます。このトレイトオブジェクトは、型消去を通じて、動的に型を決定します。Rustの<code>dyn</code>を使うことで、異なる型が共通のトレイトに従う限り、同じように扱うことができます。
;例:
:<syntaxhighlight lang=rust copy>
trait Speaker {
fn speak(&self);
}
struct Dog;
struct Cat;
impl Speaker for Dog {
fn speak(&self) {
println!("Woof!");
}
}
impl Speaker for Cat {
fn speak(&self) {
println!("Meow!");
}
}
fn speak_dyn(speaker: &dyn Speaker) {
speaker.speak();
}
fn main() {
let dog = Dog;
let cat = Cat;
speak_dyn(&dog); // Woof!
speak_dyn(&cat); // Meow!
}
</syntaxhighlight>
この例では、<code>&dyn Speaker</code>は型消去を使用して、<code>Dog</code>や<code>Cat</code>の型情報を隠蔽し、共通のインターフェースを通じてメソッドを呼び出します。
==== Goのインターフェース ====
Goでは、インターフェースを利用することで、型消去を実現します。Goのインターフェース型は、動的型付けを利用し、実行時に型情報が解決されます。
;例:
:<syntaxhighlight lang=go copy>
package main
import "fmt"
type Speaker interface {
Speak()
}
type Dog struct{}
type Cat struct{}
func (d Dog) Speak() {
fmt.Println("Woof!")
}
func (c Cat) Speak() {
fmt.Println("Meow!")
}
func main() {
var speaker Speaker
speaker = Dog{}
speaker.Speak() // "Woof!"が出力される
speaker = Cat{}
speaker.Speak() // "Meow!"が出力される
}
</syntaxhighlight>
このコードでは、<code>Speaker</code>インターフェース型に格納されるのは、実際の型情報を消去した後のオブジェクトです。ランタイムで実際の型が決まり、その型に基づいてメソッドがディスパッチされます。
==== Objective-Cの<code>id</code> ====
Objective-Cでは、<code>id</code>型を使うことで型消去を行います。<code>id</code>型は、どんな型のオブジェクトでも格納でき、実行時に型が決定され、対応するメソッドが動的に呼び出されます。
;例:
:<syntaxhighlight lang=objc copy>
#import <Foundation/Foundation.h>
@protocol Speaker
- (void)speak;
@end
@interface Dog : NSObject <Speaker>
- (void)speak;
@end
@implementation Dog
- (void)speak {
NSLog(@"Woof!");
}
@end
@interface Cat : NSObject <Speaker>
- (void)speak;
@end
@implementation Cat
- (void)speak {
NSLog(@"Meow!");
}
@end
int main() {
id<Speaker> speaker;
speaker = [[Dog alloc] init];
[speaker speak]; // "Woof!"が出力される
speaker = [[Cat alloc] init];
[speaker speak]; // "Meow!"が出力される
return 0;
}
</syntaxhighlight>
:<syntaxhighlight lang=$1 copy>id<Speaker></code>は型消去を通じて、どんな型でも格納でき、動的にメソッドを呼び出すことができます。
=== 型消去の利点 ===
型消去を利用することで、次のような利点があります:
* '''柔軟性''': 異なる型を共通のインターフェースで扱うことができ、コードが柔軟になります。
* '''動的ポリモーフィズム''': 異なる型でも、共通のインターフェースを使って動的に振る舞いを変えることができます。
* '''簡潔なコード''': 型情報を消去することで、冗長な型の記述を避け、コードが簡潔になります。
=== 型消去の欠点 ===
型消去にはいくつかの欠点もあります:
* '''ランタイムのオーバーヘッド''': 型消去を利用することで、型の解決やメソッドディスパッチがランタイムで行われるため、パフォーマンスに若干のオーバーヘッドが生じることがあります。
* '''型安全性の低下''': 型情報を消去するため、型チェックがコンパイル時に行われず、実行時にエラーが発生するリスクがあります。
=== まとめ ===
型消去は、動的ポリモーフィズムを実現するための強力な手法であり、異なる型を共通のインターフェースを通じて柔軟に扱うことができます。Rust、Go、Objective-Cなど、異なる言語で実装されており、各言語における型消去の実装は少し異なりますが、共通する概念として、型情報をランタイムで解決し、動的な振る舞いを実現することができます。
型消去は、コードの柔軟性を高め、異なる型を統一的に扱うことができる一方で、ランタイムオーバーヘッドや型安全性の低下といった注意点もあります。これらを理解し、適切に利用することで、効果的なプログラムを書くことができるようになります。
{{DEFAULTSORT:かたしようきよ}}
[[Category:プログラミング]]
h5buytwq0ey6glzqyl2mtsah4ahp9id
プログラミング/依存性の注入
0
43512
264546
2024-11-30T05:43:10Z
Ef3
694
ページの作成:「== 依存性の注入 (Dependency Injection) == 依存性の注入(DI)は、オブジェクト指向プログラミングにおけるデザインパターンの一つで、オブジェクトがその依存するコンポーネントやサービスを直接作成・管理するのではなく、外部から提供(注入)される仕組みを指します。このアプローチにより、コードの柔軟性、テストのしやすさ、モジュール性…」
264546
wikitext
text/x-wiki
== 依存性の注入 (Dependency Injection) ==
依存性の注入(DI)は、オブジェクト指向プログラミングにおけるデザインパターンの一つで、オブジェクトがその依存するコンポーネントやサービスを直接作成・管理するのではなく、外部から提供(注入)される仕組みを指します。このアプローチにより、コードの柔軟性、テストのしやすさ、モジュール性が向上します。
=== 依存性の注入の概要 ===
依存性の注入では、オブジェクトが必要とする依存関係(他のクラスやインターフェースなど)を、コンストラクタ、メソッド、またはプロパティを通じて外部から注入します。これにより、オブジェクトが直接依存関係を管理することなく、必要なサービスを利用できるようになります。
=== 依存性の注入の種類 ===
依存性の注入には主に以下の3つの方法があります。
# '''コンストラクタインジェクション (Constructor Injection)'''
#: コンストラクタインジェクションでは、依存関係はオブジェクトのコンストラクタを通じて注入されます。これにより、依存関係が不変であり、オブジェクトの作成時に必ず注入されることが保証されます。
# '''セッターインジェクション (Setter Injection)'''
#: セッターインジェクションでは、オブジェクトが作成された後、依存関係がセッターメソッドを通じて設定されます。この方法は、オブジェクトのライフサイクル中に依存関係を変更できる柔軟性を提供します。
# '''インターフェースインジェクション (Interface Injection)'''
#: インターフェースインジェクションでは、依存関係を注入するためのメソッドがインターフェースとして定義され、クラスがそのインターフェースを実装することで依存関係を注入します。
=== 依存性の注入の利点 ===
依存性の注入を使用することで、以下のような利点があります。
* '''疎結合''' - クラス同士の結びつきが弱くなり、変更に対して柔軟性を持たせることができる。
* '''テストの容易さ''' - 依存関係をモックオブジェクトやスタブに差し替えることで、単体テストを行いやすくなる。
* '''コードの再利用性''' - 依存関係が外部から注入されるため、異なるコンポーネント間で再利用しやすくなる。
* '''可読性の向上''' - 依存関係が明示的に示され、コードが直感的に理解しやすくなる。
=== 依存性の注入の使用例 ===
以下は、コンストラクタインジェクションを用いた依存性の注入のシンプルな例です。
:<syntaxhighlight lang=java copy>
public class DatabaseService {
private DatabaseConnection connection;
// コンストラクタで依存関係を注入
public DatabaseService(DatabaseConnection connection) {
this.connection = connection;
}
public void query(String sql) {
connection.executeQuery(sql);
}
}
public class Application {
public static void main(String[] args) {
// 外部で依存関係を作成して注入
DatabaseConnection connection = new MySQLConnection();
DatabaseService service = new DatabaseService(connection);
service.query("SELECT * FROM users");
}
}
</syntaxhighlight>
この例では、<code>DatabaseService</code>は<code>DatabaseConnection</code>に依存していますが、その依存関係はコンストラクタを通じて注入されています。この方法では、<code>DatabaseService</code>はどのデータベース接続を使用するかを意識することなく、抽象化されたインターフェースで操作を行います。
=== まとめ ===
依存性の注入は、オブジェクト指向プログラミングの中で重要な役割を果たす設計パターンであり、システムのモジュール性、テスト可能性、柔軟性を高めるために広く使用されています。特に大規模なシステムや複雑な依存関係が存在するアプリケーションにおいて、DIを活用することでコードの品質が向上します。
{{DEFAULTSORT:いそんせいのちゆうにゆう}}
[[Category:プログラミング]]
9wctlpb85jate3qeffw8f3dihsy97vl
プログラミング/依存性逆転の原則
0
43513
264547
2024-11-30T05:53:12Z
Ef3
694
依存性逆転の原則(Dependency Inversion Principle, DIP)は、ソフトウェアの設計における重要な原則で、ロバート・C・マーチン(通称:Uncle Bob)によって提唱されたSOLID原則の一部です。この原則は、モジュール間の依存関係を適切に管理し、システムの柔軟性と可変性を高めることを目的としています。
264547
wikitext
text/x-wiki
== 依存性逆転の原則 (Dependency Inversion Principle) ==
依存性逆転の原則(Dependency Inversion Principle, DIP)は、ソフトウェアの設計における重要な原則で、ロバート・C・マーチン(通称:Uncle Bob)によって提唱されたSOLID原則の一部です。この原則は、モジュール間の依存関係を適切に管理し、システムの柔軟性と可変性を高めることを目的としています。
=== 依存性逆転の原則の概要 ===
依存性逆転の原則は、次の2つの指針から成り立っています。
# 高水準モジュール(ビジネスロジックなど)は、低水準モジュール(データベースやファイルシステムなど)に依存すべきではない。
# 両者は、抽象(インターフェースや抽象クラス)に依存すべきであり、具体的な実装に依存してはならない。
この原則は、依存関係を「抽象に依存させる」ことを推奨しており、具体的な実装から抽象化されたインターフェースやクラスに依存させることで、コードの柔軟性と再利用性を高めます。
=== 依存性逆転の原則の利点 ===
依存性逆転の原則を適用することによる利点は以下の通りです。
* '''変更に強いコード''' - 高水準モジュールと低水準モジュールが分離されるため、どちらかのモジュールに変更があっても、もう一方のモジュールには影響を与えません。
* '''テスト容易性の向上''' - 抽象に依存することで、モジュールを容易にモック化やスタブ化して単体テストを行うことができます。
* '''コードの再利用性''' - 高水準モジュールが低水準モジュールに依存しないため、異なる実装を簡単に差し替えられ、システム全体の再利用性が向上します。
* '''柔軟性の向上''' - システム全体を柔軟に設計することができ、異なるコンポーネントやサービスを簡単に組み合わせることができます。
=== 依存性逆転の原則の適用方法 ===
依存性逆転の原則を適用するには、次のような手順を踏むことが一般的です。
# '''抽象化の導入'''
#: 高水準モジュールと低水準モジュールの間にインターフェースや抽象クラスを作成します。このインターフェースや抽象クラスを通じて、具体的な実装が依存されます。
# '''依存性の注入 (DI) を使用する'''
#: 高水準モジュールに必要な依存関係を外部から注入することで、低水準モジュールの実装が変更された場合でも、高水準モジュールのコードを変更せずに対応できるようにします。コンストラクタインジェクションやセッターインジェクションが有効です。
# '''具体的な実装を切り離す'''
#: 低水準モジュールの具体的な実装をクラス内で作成せず、外部で実装された具体クラスを注入します。これにより、高水準モジュールは実装に依存することなく、インターフェースを通じて機能を利用できます。
=== 依存性逆転の原則の例 ===
以下に、依存性逆転の原則を適用したシンプルな例を示します。
:<syntaxhighlight lang=java copy>
// 低水準モジュール
public class MySQLDatabase implements Database {
public void connect() {
System.out.println("MySQL database connected");
}
}
// 高水準モジュール
public class Application {
private Database database;
// コンストラクタで依存関係を注入
public Application(Database database) {
this.database = database;
}
public void run() {
database.connect();
}
}
// 抽象化(インターフェース)
public interface Database {
void connect();
}
public class Main {
public static void main(String[] args) {
// 依存性逆転の原則に基づき、低水準モジュールを外部で注入
Database database = new MySQLDatabase();
Application app = new Application(database);
app.run();
}
}
</syntaxhighlight>
この例では、<code>Application</code>クラスは<code>Database</code>インターフェースに依存しており、具体的なデータベースの実装(<code>MySQLDatabase</code>など)には依存していません。これにより、データベースの実装を変更しても、<code>Application</code>クラスは変更する必要がなくなります。
=== まとめ ===
依存性逆転の原則は、ソフトウェアの設計において、コードの柔軟性、再利用性、テスト容易性を向上させるための重要な原則です。高水準モジュールと低水準モジュールを抽象化して依存関係を管理することで、変更に強い、メンテナンス性の高いコードを実現できます。この原則を適切に適用することで、ソフトウェアアーキテクチャの品質を大きく向上させることができます。
{{DEFAULTSORT:いそんせいきやくてんのけんそく}}
[[Category:プログラミング]]
7gedwti14mr6s85zw4vbjc1s7wbt3vu
高等学校言語文化/唐詩紀事
0
43514
264549
2024-11-30T07:31:53Z
~2024-21056
85136
ページの作成:「{{Pathnav|高等学校の学習|高等学校国語|高等学校言語文化|frame=1}} ==推敲== {{wikisource|zh:唐詩紀事 (四庫全書本)|唐詩紀事}} ===白文 (正字)=== :賈島赴擧至京。騎驢賦詩、得「僧推月下門」之句。慾改推作敲、引手作推敲之勢、未決。不覺衝大尹韓愈。乃具󠄁言、愈曰、「敲字佳矣。」遂󠄂竝轡論詩久之。 <!-->入力環境の関係で正体字になっていない箇所が…」
264549
wikitext
text/x-wiki
{{Pathnav|高等学校の学習|高等学校国語|高等学校言語文化|frame=1}}
==推敲==
{{wikisource|zh:唐詩紀事 (四庫全書本)|唐詩紀事}}
===白文 (正字)===
:賈島赴擧至京。騎驢賦詩、得「僧推月下門」之句。慾改推作敲、引手作推敲之勢、未決。不覺衝大尹韓愈。乃具󠄁言、愈曰、「敲字佳矣。」遂󠄂竝轡論詩久之。
<!-->入力環境の関係で正体字になっていない箇所があります。<-->
(唐詩起事 巻四十 賈島)
===書き下し (新字)===
:{{ruby|賈島|かとう}}{{ruby|挙|きよ}}に{{ruby|赴|おもむ}}きて{{ruby|京|けい}}に{{ruby|至|いた}}り、{{ruby|驢|ろ}}に{{ruby|騎|の}}りて詩を{{ruby|賦|ふ}}して、「僧は推す月下の門を」の句を得たり。推すを改めて{{ruby|敲|たた}}くと{{ruby|作|な}}さんと欲し、手を引きて推敲の{{ruby|勢|いきほひ}}を作すも、未だ{{ruby|決|けつ}}せず。覚えず{{ruby|大尹|たいゐん}}{{ruby|韓愈|かんゆ}}に{{ruby|衝|あ}}たる。{{ruby|乃|すなは}}ち{{ruby|具|つぶさ}}に言ふに、愈{{ruby|曰|い}}はく、「敲くの字{{ruby|佳|よ}}し」と。遂に{{ruby|轡|たづな}}を並べて詩を論ずること{{ruby|之|これ}}を久しくす。
====注釈====
*賈島:中唐の詩人。西暦779〜843年。
*挙:科挙(官吏登用試験)のこと。詩が試験科目の一つであった。
*京:ここでは、唐の都である長安。
*驢:ロバ。
*賦す:詩を作る。
*推す:「押す」に同じ。
*敲く:「叩く」に同じ。
*引く:伸ばす。
*勢:様子。
*覚えず:思わず。うっかり。
*大尹:高官。
*韓愈:中唐の詩人。西暦768〜824年。唐宋八大家に数えられる。
*衝たる:突っ込む
*具に:詳しく
*轡を並べる:乗り物を同じ方向に向ける。「轡」は「くつわ」とも読む。
===現代語訳===
:賈島は科挙の試験を受けるために都・長安に来て、ロバに乗りながら詩を作っていると、「僧は月下の門を推す」という句ができた。しかし、この「推す」を「敲く」に改めたいと思い、手を伸ばして推す・敲くの仕草をしたが、まだ決まらない。そうしているうちに、思わず都の長官である韓愈の隊列に突っ込んでしまった。そこで、韓愈に突っ込んでしまった理由を話すと、「敲という文字が良い。」と返ってきた。そのまま二人は轡を並べ、進みながら詩についてしばらく論じた。
===鑑賞===
『唐詩紀事』は、12世紀に北宋の計有功によって編纂された。全八十一巻からなり、唐代の詩人1500人の略伝・逸話・代表作を収めている。
この『推敲』は、『唐詩紀事』第四十巻「賈島」の終盤の抜粋であり、この文章の内容から、「推敲」は「詞や文章を書いた後、より良いものにするため何度も練り直すこと」という意味になっている。
『唐詩紀事』の本文の続きには、以下のように記されている。
:或云、吟「落葉滿長安」之句、唐突大尹劉栖楚被繋、一夕放之。
:({{ruby|或|あるひと}}{{ruby|云|い}}はく、「落葉長安に満つ」の句を{{ruby|吟|うた}}ふ、大尹劉栖楚に唐突し繋がれるも、一夕にして之を放つ。)
現代語訳すると、『ある人が言うには、賈島は「落葉長安に満つ」という句を口ずさみながら、高官である劉栖楚の乗っている馬にぶつかって牢屋に入れられた。しかし、(牢屋の中でも句を苦吟して「秋風渭水に吹き 落葉長安に満つ」という優れた句を作り出したので、)一夜にして釈放された。』となる。
[[カテゴリ:高等学校教育 国語]]
[[カテゴリ:日本語]]
[[カテゴリ:漢文]]
dbcnl5x13x3pe577oamgkjmx0u8z04x