現在のブログ
ゲーム開発ブログ (2025年~) Gamedev Blog (2025~)
レガシーブログ
テクノロジーブログ (2018~2024年) リリースノート (2023~2025年) MeatBSD (2024年)
【Rust】Rust言語のレビュー
Rust言語を好まない事で知られているあたしは、主に狂った様なコミュニティのせいですが、其れ以外にもABIが頻繁かつ高速に変わる事、「何をするにもサードパーティの依存関係を使え」というJavaScript的な思考、そして非常に遅いコンパイル時間も嫌いです。
然し、少なくとも一度は実際に使ってみて、技術的なレベルでも判断すべきだと思い、試してみる事にしました。
ゲーム開発者として、OpenGLレンダラーをRustで書いてみます。
OpenGLレンダラーは、ゲーム開発者にとってのTODOウェブサイトの様な物です。
此のレビューは以前のOdinレビューと非常に似た形式になりますが、「こんにちは、世界!」の部分は省略し、今回はFreeBSDではなくWindows上で行います。
念の為、あたしはC言語とC++のプログラマーなので、当然Rustに対して多少のバイアスがかかっている事はご了承下さい。
其れでも、此の記事は出来る限り中立的に保ち、技術的な観点からのみ褒めたり批判したりするつもりです。
インストール
早速問題にぶつかりました。
Rustをインストールする前からRustの問題に遭遇です!
ドキュメントではrustup-init.exeをダウンロードして実行するよう指示されています。
其れはよくある方法ですが、実行するとBATスクリプトの様に振る舞います。
LinuxやmacOSでは、cURLでリモートのスクリプトをダウンロードし、POSIX Shellにパイプして実行する様になっています。
此れがどれだけ狂っているか分かりますか!?
Rustは自分を「究極の、完璧なメモリ安全言語」と宣伝していますが、インストール方法は文字通りセキュリティ上の脆弱性です。
サイバーセキュリティを学び始めた人は皆、何度も「リモートのスクリプトを盲目的に信用するな、実行する前に1行1行読め」と警告されます。
curl https://sh.rustup.rs | lessで事前に内容を確認してから実行する事は出来ますが、ドキュメントでは其れを推奨しておらず、只単に実行するよう書かれています。
例え公式ソースからであっても、手動で検証すべきです。
攻撃者によってリモートで改ざんされる可能性があるからです(以前のLinux Mintの様に)。
あたしは検証した所、執筆時点では怪しい物は見つからなかったので、其のまま実行しました。
此の記事を将来読む人の為に、使ったRustのバージョンは1.94.1です。
執筆時点では最新版でしたが、Rustの更新速度を考えると、記事公開時には既に古くなっているかもしん。
初期設定
どの言語のプロジェクトでもあんま好きな部分ではありませんが、通常は一度だけやれば済むので我慢出来ます。
> cd .\dev\stub
> cargo new opengl-render-rs
cargo new opengl-render-rs
Creating binary (application) `opengl-render-rs` package
note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
> cd .\opengl-render-rs
> ls
ディレクトリ: C:\Users\suwako\dev\stub\opengl-render-rs
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 2026/04/10 17:34 src
-a---- 2026/04/10 17:34 8 .gitignore
-a---- 2026/04/10 17:34 87 Cargo.toml
> nvim .\Cargo.toml
此処までは順調です。
然し、此処で残念な問題に遭遇しました。
依存関係の扱いがC、C++、Go、Zigとは全く異なります。
Cargo.tomlというファイルがあり、「モダンな」Web開発を強く思い出させます。
Rust公式ドキュメントが、Rust開発者が日常的に行う依存関係の追加方法について殆ど役に立たないのも問題です。
オンラインのガイドを見ると、一部の依存関係はスコープ内に情報を書く必要があり、他の物は文字列でバージョン番号だけを書くだけで済む物があり、どちらを使うべきかの説明がありません。
[dependencies]
gl = "0.14.0"
glfw = "0.62.0"
> cargo build
Updating crates.io index
Locking 125 packages to latest Rust 1.94.1 compatible versions
Downloaded aligned v0.4.3
Downloaded avif-serialize v0.8.8
Downloaded core2 v0.4.0
Downloaded new_debug_unreachable v1.0.6
Downloaded zune-inflate v0.2.54
Downloaded zune-core v0.5.1
Downloaded zune-jpeg v0.5.15
Downloaded quote v1.0.45
Downloaded y4m v0.8.0
Downloaded simd-adler32 v0.3.9
Downloaded v_frame v0.3.9
Downloaded thiserror-impl v2.0.18
Downloaded crc32fast v1.5.0
Downloaded memchr v2.8.0
Downloaded smallvec v1.15.1
Downloaded bitflags v2.11.0
Downloaded xml-rs v0.8.28
Downloaded qoi v0.4.1
Downloaded zerocopy-derive v0.8.48
Downloaded tiff v0.11.3
Downloaded cc v1.2.59
Downloaded png v0.18.1
Downloaded moxcms v0.8.1
Downloaded itertools v0.14.0
Downloaded rayon v1.11.0
Downloaded nom v8.0.0
Downloaded exr v1.74.0
Downloaded glfw v0.62.0
Downloaded num-bigint v0.4.6
Downloaded imgref v1.12.0
Downloaded zerocopy v0.8.48
Downloaded syn v2.0.117
Downloaded image v0.25.10
Downloaded flate2 v1.1.9
Downloaded bumpalo v3.20.2
Downloaded rayon-core v1.13.0
Downloaded av-scenechange v0.14.1
Downloaded proc-macro2 v1.0.106
Downloaded bitstream-io v4.9.0
Downloaded image-webp v0.2.4
Downloaded wasm-bindgen-macro-support v0.2.117
Downloaded wasm-bindgen v0.2.117
Downloaded unicode-ident v1.0.24
Downloaded num-traits v0.2.19
Downloaded half v2.7.1
Downloaded crossbeam-epoch v0.9.18
Downloaded bytemuck v1.25.0
Downloaded rustversion v1.0.22
Downloaded khronos_api v3.1.0
Downloaded once_cell v1.21.4
Downloaded log v0.4.29
Downloaded getrandom v0.3.4
Downloaded crossbeam-utils v0.8.21
Downloaded built v0.8.0
Downloaded bitflags v1.3.2
Downloaded either v1.15.0
Downloaded cmake v0.1.58
Downloaded num-rational v0.4.2
Downloaded libc v0.2.184
Downloaded thiserror v2.0.18
Downloaded shlex v1.3.0
Downloaded rgb v0.8.53
Downloaded pxfm v0.1.28
Downloaded raw-window-handle v0.6.2
Downloaded ravif v0.13.0
Downloaded num-integer v0.1.46
Downloaded profiling-procmacros v1.0.17
Downloaded pkg-config v0.3.32
Downloaded glfw-sys v8.0.0
Downloaded paste v1.0.15
Downloaded miniz_oxide v0.8.9
Downloaded jobserver v0.1.34
Downloaded gl_generator v0.14.0
Downloaded gl v0.14.0
Downloaded fdeflate v0.3.7
Downloaded color_quant v1.1.0
Downloaded wasm-bindgen-shared v0.2.117
Downloaded wasm-bindgen-macro v0.2.117
Downloaded stable_deref_trait v1.2.1
Downloaded profiling v1.0.17
Downloaded winapi v0.3.9
Downloaded pastey v0.1.1
Downloaded weezl v0.1.12
Downloaded simd_helpers v0.1.0
Downloaded quick-error v2.0.1
Downloaded loop9 v0.1.5
Downloaded fax v0.2.6
Downloaded byteorder-lite v0.1.0
Downloaded bit_field v0.10.3
Downloaded num-derive v0.4.2
Downloaded noop_proc_macro v0.3.0
Downloaded maybe-rayon v0.1.1
Downloaded gif v0.14.2
Downloaded equator-macro v0.4.2
Downloaded rav1e v0.8.1
Downloaded equator v0.4.2
Downloaded lebe v0.5.3
Downloaded find-msvc-tools v0.1.9
Downloaded fax_derive v0.2.0
Downloaded crossbeam-deque v0.8.6
Downloaded cfg-if v1.0.4
Downloaded av1-grain v0.2.5
Downloaded autocfg v1.5.0
Downloaded as-slice v0.2.1
Downloaded arrayvec v0.7.6
Downloaded anyhow v1.0.102
Downloaded arg_enum_proc_macro v0.3.4
Downloaded aligned-vec v0.6.4
Downloaded adler2 v2.0.1
Downloaded 109 crates (11.1MiB) in 10.33s (largest was `rav1e` at 1.4MiB)
Compiling proc-macro2 v1.0.106
Compiling unicode-ident v1.0.24
Compiling quote v1.0.45
Compiling autocfg v1.5.0
Compiling crossbeam-utils v0.8.21
Compiling cfg-if v1.0.4
Compiling rayon-core v1.13.0
Compiling log v0.4.29
Compiling simd-adler32 v0.3.9
Compiling memchr v2.8.0
Compiling anyhow v1.0.102
Compiling either v1.15.0
Compiling zerocopy v0.8.48
Compiling arrayvec v0.7.6
Compiling stable_deref_trait v1.2.1
Compiling thiserror v2.0.18
Compiling crc32fast v1.5.0
Compiling adler2 v2.0.1
Compiling miniz_oxide v0.8.9
Compiling as-slice v0.2.1
Compiling shlex v1.3.0
Compiling paste v1.0.15
Compiling khronos_api v3.1.0
Compiling built v0.8.0
Compiling libc v0.2.184
Compiling av-scenechange v0.14.1
Compiling num-traits v0.2.19
Compiling find-msvc-tools v0.1.9
Compiling nom v8.0.0
Compiling core2 v0.4.0
Compiling aligned v0.4.3
Compiling quick-error v2.0.1
Compiling rav1e v0.8.1
Compiling pastey v0.1.1
Compiling cc v1.2.59
Compiling y4m v0.8.0
Compiling flate2 v1.1.9
Compiling bitstream-io v4.9.0
Compiling itertools v0.14.0
Compiling zune-core v0.5.1
Compiling crossbeam-epoch v0.9.18
Compiling pkg-config v0.3.32
Compiling imgref v1.12.0
Compiling noop_proc_macro v0.3.0
Compiling new_debug_unreachable v1.0.6
Compiling weezl v0.1.12
Compiling xml-rs v0.8.28
Compiling crossbeam-deque v0.8.6
Compiling loop9 v0.1.5
Compiling zune-jpeg v0.5.15
Compiling avif-serialize v0.8.8
Compiling syn v2.0.117
Compiling num-integer v0.1.46
Compiling cmake v0.1.58
Compiling simd_helpers v0.1.0
Compiling zune-inflate v0.2.54
Compiling fdeflate v0.3.7
Compiling bytemuck v1.25.0
Compiling num-bigint v0.4.6
Compiling gl_generator v0.14.0
Compiling bit_field v0.10.3
Compiling smallvec v1.15.1
Compiling rayon v1.11.0
Compiling rgb v0.8.53
Compiling lebe v0.5.3
Compiling winapi v0.3.9
Compiling bitflags v2.11.0
Compiling pxfm v0.1.28
Compiling color_quant v1.1.0
Compiling byteorder-lite v0.1.0
Compiling qoi v0.4.1
Compiling gif v0.14.2
Compiling png v0.18.1
Compiling image-webp v0.2.4
Compiling glfw-sys v8.0.0
Compiling raw-window-handle v0.6.2
Compiling bitflags v1.3.2
Compiling gl v0.14.0
Compiling num-rational v0.4.2
Compiling maybe-rayon v0.1.1
Compiling equator-macro v0.4.2
Compiling zerocopy-derive v0.8.48
Compiling thiserror-impl v2.0.18
Compiling arg_enum_proc_macro v0.3.4
Compiling profiling-procmacros v1.0.17
Compiling num-derive v0.4.2
Compiling fax_derive v0.2.0
Compiling moxcms v0.8.1
Compiling profiling v1.0.17
Compiling fax v0.2.6
Compiling equator v0.4.2
Compiling aligned-vec v0.6.4
Compiling v_frame v0.3.9
Compiling av1-grain v0.2.5
Compiling half v2.7.1
Compiling tiff v0.11.3
Compiling exr v1.74.0
Compiling ravif v0.13.0
Compiling image v0.25.10
Compiling glfw v0.62.0
Compiling opengl-render-rs v0.1.0 (C:\Users\suwako\dev\stub\opengl-render-rs)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 53.00s
此れで89個の「crate」をダウンロードし、135個をコンパイルしました。
何故こんなに沢山必要なのでしょうか?
OpenGLは只のOpenGLのはずで、精々GLEWかGLAD、そしてGLFWは使用しているウィンドウAPIだけのはずです。
其れなのに、用途の分からない大量のジャンクをダウンロードしてしまいました。
「モダンな」開発者は、あたしが「大量の依存関係に頼るのは非常に危険だ」と言うと怒ります。
此の135個の依存関係の其々が、潜在的な攻撃ベクター(依存関係の複雑さによっては複数)です。
此れはRust開発者だけに向けた批判ではなく、PHP、Javascript、Go、Zig、C、C++、C#、Java等、ほぼ全ての言語に向けた物です。
「でもRustは本質的に安全だ」と反論するかもしんが、上に積み重ねられた余計な物はサプライチェーン攻撃で悪用される可能性があります。
どの言語で書かれていようと関係ありません。
「メモリ安全」な言語を使っているからといって、無用心でいい理由にはなりません。
若し此の記事を書きながらRustを初めて学んでいなければ、glとglfwがインポートしている物の殆どを使わない自前の依存関係を作っていたでしょう。
ウインドウの作成
extern crate glfw;
use glfw::{Action, Context, Key};
fn main() {
use glfw::fail_on_errors;
let mut glfw = glfw::init(fail_on_errors!()).unwrap();
let (mut window, events) =
glfw.create_window(800, 600, "OpenGLレンダー", glfw::WindowMode::Windowed)
.expect("GLFWウィンドウを作成に失敗。");
window.make_current();
window.set_key_polling(true);
while !window.should_close() {
window.swap_buffers();
glfw.poll_events();
for (_, event) in glfw::flush_messages(&events) {
println!("{:?}", event);
match event {
glfw::WindowEvent::Key(Key::Q, _, Action::Press, _) => {
window.set_should_close(true)
},
_ => {},
}
}
}
}
> cargo build
Compiling opengl-render-rs v0.1.0 (C:\Users\suwako\dev\stub\opengl-render-rs)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.37s
> .\target\debug\opengl-render-rs.exe
Key(Q, 16, Press, (empty))
コード自体は悪くありません。
驚く程短く、どこにも明示的な解放処理がなく、高レベル言語の様に感じます。
create_windowの行が長くなり過ぎるのが嫌なので、任意の1行88文字制限に収める為に3行に分けました。
コード自体に関しては、全く悪くないです。
次の作業はWSL2内で行う必要があります。
$ cd /mnt/c/Users/suwako/dev/stub/opengl-render-rs
$ ldd target/debug/opengl-render-rs.exe
動的実行ファイルではありません
$ file target/debug/opengl-render-rs.exe
target/debug/opengl-render-rs.exe: PE32+ executable for MS Windows 6.00 (console), x86-64, 5 sections
静的バイナリとしてコンパイルされたのは本当に良い点です。
コンパイラがデフォルトで静的リンクしてくれるのが好きです。
本格的なレンダリングを始めましょう。
Odinレビューでやった事とほぼ同じ事をしますが、今回はOpenGL 3.3ではなく4.6を使います。
OpenGLが動くか確認してみましょう。
extern crate glfw;
extern crate gl;
use glfw::{Action, Context, Key};
use gl::types::*;
const VERTEX_SRC: &str = r#"
#version 460 core
layout (location = 0) in vec3 aPos;
void main() {
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}
"#;
const FRAG_SRC: &str = r#"
#version 460 core
out vec4 FragColor;
void main() {
FragColor = vec4(1.f, .5f, .2f, 1.f);
}
"#;
fn main() {
use glfw::fail_on_errors;
let mut glfw = glfw::init(glfw::fail_on_errors!()).unwrap();
glfw.window_hint(glfw::WindowHint::ContextVersionMajor(4));
glfw.window_hint(glfw::WindowHint::ContextVersionMinor(6));
glfw.window_hint(glfw::WindowHint::OpenGlProfile(glfw::OpenGlProfileHint::Core));
let (mut window, events) =
glfw.create_window(800, 600, "OpenGLレンダー", glfw::WindowMode::Windowed)
.expect("GLFWウィンドウを作成に失敗。");
window.make_current();
gl::load_with(|s| {
window.get_proc_address(s)
.map_or(std::ptr::null(), |f| f as *const _)
});
window.set_key_polling(true);
window.set_framebuffer_size_callback(|_wnd, w, h| {
unsafe { gl::Viewport(0, 0, w, h); }
});
while !window.should_close() {
unsafe {
gl::ClearColor(1.0, 0.2, 0.9, 1.0);
gl::Clear(gl::COLOR_BUFFER_BIT);
}
window.swap_buffers();
glfw.poll_events();
for (_, event) in glfw::flush_messages(&events) {
match event {
glfw::WindowEvent::Key(Key::Q, _, Action::Press, _) => {
window.set_should_close(true)
},
_ => {},
}
}
}
}
> cargo build
Compiling opengl-render-rs v0.1.0 (C:\Users\suwako\dev\stub\opengl-render-rs)
warning: unused import: `gl::types::*`
--> src\main.rs:4:5
|
4 | use gl::types::*;
| ^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default
warning: constant `VERTEX_SRC` is never used
--> src\main.rs:6:7
|
6 | const VERTEX_SRC: &str = r#"
| ^^^^^^^^^^
|
= note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default
warning: constant `FRAG_SRC` is never used
--> src\main.rs:16:7
|
16 | const FRAG_SRC: &str = r#"
| ^^^^^^^^
warning: `opengl-render-rs` (bin "opengl-render-rs") generated 3 warnings (run `cargo fix --bin "opengl-render-rs" -p opengl-render-rs` to apply 1 suggestion)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.45s
> .\target\debug\opengl-render-rs.exe
動きました!
然し、未だシェーダーを何もしていないので、次に其れを実装します。
いや、其の前に別の点を指摘しておきます:
extern crate glfw;
extern crate gl;
use glfw::{Action, Context, Key};
use gl::types::*;
use std::ffi::CString;
use std::ptr;
const VERTEX_SRC: &str = r#"
#version 460 core
layout (location = 0) in vec3 aPos;
void main() {
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}
"#;
const FRAG_SRC: &str = r#"
#version 460 core
out vec4 FragColor;
void main() {
FragColor = vec4(1.f, .5f, .2f, 1.f);
}
"#;
fn main() {
use glfw::fail_on_errors;
let mut glfw = glfw::init(glfw::fail_on_errors!()).unwrap();
glfw.window_hint(glfw::WindowHint::ContextVersionMajor(4));
glfw.window_hint(glfw::WindowHint::ContextVersionMinor(6));
glfw.window_hint(glfw::WindowHint::OpenGlProfile(glfw::OpenGlProfileHint::Core));
let (mut window, events) =
glfw.create_window(800, 600, "OpenGLレンダー", glfw::WindowMode::Windowed)
.expect("GLFWウィンドウを作成に失敗。");
window.make_current();
gl::load_with(|s| {
window.get_proc_address(s)
.map_or(std::ptr::null(), |f| f as *const _)
});
window.set_key_polling(true);
window.set_framebuffer_size_callback(|_wnd, w, h| {
unsafe { gl::Viewport(0, 0, w, h); }
});
unsafe {
let vertexShader = gl::CreateShader(gl::VERTEX_SHADER);
let cVtx = CString::new(VERTEX_SRC.as_bytes())
.expect("頂点シェーダー向けCStringに変換に失敗。");
gl::ShaderSource(vertexShader, 1, &cVtx.as_ptr(), ptr::null());
gl::CompileShader(vertexShader);
let mut success = gl::FALSE as GLint;
gl::GetShaderiv(vertexShader, gl::COMPILE_STATUS, &mut success);
if success == gl::FALSE as GLint {
let mut len = 0;
gl::GetShaderiv(vertexShader, gl::INFO_LOG_LENGTH, &mut len);
let mut infoLog = vec![0u8; len as usize];
gl::GetShaderInfoLog(vertexShader, len, ptr::null_mut(),
infoLog.as_mut_ptr() as *mut GLchar);
let log = String::from_utf8_lossy(&infoLog);
panic!("頂点シェーダーコンパイルに失敗。\n{}", log);
}
let fragShader = gl::CreateShader(gl::FRAGMENT_SHADER);
let cFrag = CString::new(FRAG_SRC.as_bytes())
.expect("フラグメントシェーダー向けCStringに変換に失敗。");
gl::ShaderSource(fragShader, 1, &cFrag.as_ptr(), ptr::null());
gl::CompileShader(fragShader);
gl::GetShaderiv(fragShader, gl::COMPILE_STATUS, &mut success);
if success == gl::FALSE as GLint {
let mut len = 0;
gl::GetShaderiv(fragShader, gl::INFO_LOG_LENGTH, &mut len);
let mut infoLog = vec![0u8; len as usize];
gl::GetShaderInfoLog(fragShader, len, ptr::null_mut(),
infoLog.as_mut_ptr() as *mut GLchar);
let log = String::from_utf8_lossy(&infoLog);
panic!("フラグメントシェーダーコンパイルに失敗。\n{}", log);
}
let shaderProgram = gl::CreateProgram();
gl::AttachShader(shaderProgram, vertexShader);
gl::AttachShader(shaderProgram, fragShader);
gl::LinkProgram(shaderProgram);
gl::GetProgramiv(shaderProgram, gl::LINK_STATUS, &mut success);
if success == gl::FALSE as GLint {
let mut len = 0;
gl::GetShaderiv(fragShader, gl::INFO_LOG_LENGTH, &mut len);
let mut infoLog = vec![0u8; len as usize];
gl::GetShaderInfoLog(fragShader, len, ptr::null_mut(),
infoLog.as_mut_ptr() as *mut GLchar);
let log = String::from_utf8_lossy(&infoLog);
panic!("プログラムを受け取るに失敗。\n{}", log);
}
gl::DeleteShader(vertexShader);
gl::DeleteShader(fragShader);
}
while !window.should_close() {
unsafe {
gl::ClearColor(.6, .1, 0.6, 1.0);
gl::Clear(gl::COLOR_BUFFER_BIT);
}
window.swap_buffers();
glfw.poll_events();
for (_, event) in glfw::flush_messages(&events) {
match event {
glfw::WindowEvent::Key(Key::Q, _, Action::Press, _) => {
window.set_should_close(true)
},
_ => {},
}
}
}
}
cargo build
Compiling opengl-render-rs v0.1.0 (C:\Users\suwako\dev\stub\opengl-render-rs)
error: float literals must have an integer part
--> src\main.rs:111:22
|
111 | gl::ClearColor(.6, .1, 0.6, 1.0);
| ^^
|
help: must have an integer part
|
111 | gl::ClearColor(0.6, .1, 0.6, 1.0);
| +
error: float literals must have an integer part
--> src\main.rs:111:26
|
111 | gl::ClearColor(.6, .1, 0.6, 1.0);
| ^^
|
help: must have an integer part
|
111 | gl::ClearColor(.6, 0.1, 0.6, 1.0);
| +
error: could not compile `opengl-render-rs` (bin "opengl-render-rs") due to 2 previous errors
Rustは0を省略する事を許してくれないようです。
C、C++、そして先ほど見たOdinでは問題なく出来たのに・・・
然し、此れが本当の問題ではありません。
OpenGL関連のコードの殆どがunsafeブロックの中に置かれている事です。
初めてコンパイルした時のコンパイラ出力はこんな感じでした。
error[E0133]: call to unsafe function `CreateShader` is unsafe and requires unsafe block
--> src\main.rs:53:22
|
53 | let vertexShader = gl::CreateShader(gl::VERTEX_SHADER);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function `ShaderSource` is unsafe and requires unsafe block
--> src\main.rs:56:3
|
56 | gl::ShaderSource(vertexShader, 1, &cVtx.as_ptr(), ptr::null());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function `CompileShader` is unsafe and requires unsafe block
--> src\main.rs:57:3
|
57 | gl::CompileShader(vertexShader);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function `GetShaderiv` is unsafe and requires unsafe block
--> src\main.rs:60:3
|
60 | gl::GetShaderiv(vertexShader, gl::COMPILE_STATUS, &mut success);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function `GetShaderiv` is unsafe and requires unsafe block
--> src\main.rs:63:5
|
63 | gl::GetShaderiv(vertexShader, gl::INFO_LOG_LENGTH, &mut len);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function `GetShaderInfoLog` is unsafe and requires unsafe block
--> src\main.rs:65:5
|
65 | / gl::GetShaderInfoLog(vertexShader, len, ptr::null_mut(),
66 | | infoLog.as_mut_ptr() as *mut GLchar);
| |__________________________________________^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function `CreateShader` is unsafe and requires unsafe block
--> src\main.rs:71:20
|
71 | let fragShader = gl::CreateShader(gl::FRAGMENT_SHADER);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function `ShaderSource` is unsafe and requires unsafe block
--> src\main.rs:74:3
|
74 | gl::ShaderSource(fragShader, 1, &cFrag.as_ptr(), ptr::null());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function `CompileShader` is unsafe and requires unsafe block
--> src\main.rs:75:3
|
75 | gl::CompileShader(fragShader);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function `GetShaderiv` is unsafe and requires unsafe block
--> src\main.rs:77:3
|
77 | gl::GetShaderiv(fragShader, gl::COMPILE_STATUS, &mut success);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function `GetShaderiv` is unsafe and requires unsafe block
--> src\main.rs:80:5
|
80 | gl::GetShaderiv(fragShader, gl::INFO_LOG_LENGTH, &mut len);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function `GetShaderInfoLog` is unsafe and requires unsafe block
--> src\main.rs:82:5
|
82 | / gl::GetShaderInfoLog(fragShader, len, ptr::null_mut(),
83 | | infoLog.as_mut_ptr() as *mut GLchar);
| |__________________________________________^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function `CreateProgram` is unsafe and requires unsafe block
--> src\main.rs:88:23
|
88 | let shaderProgram = gl::CreateProgram();
| ^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function `AttachShader` is unsafe and requires unsafe block
--> src\main.rs:89:3
|
89 | gl::AttachShader(shaderProgram, vertexShader);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function `AttachShader` is unsafe and requires unsafe block
--> src\main.rs:90:3
|
90 | gl::AttachShader(shaderProgram, fragShader);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function `LinkProgram` is unsafe and requires unsafe block
--> src\main.rs:91:3
|
91 | gl::LinkProgram(shaderProgram);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function `GetProgramiv` is unsafe and requires unsafe block
--> src\main.rs:93:3
|
93 | gl::GetProgramiv(shaderProgram, gl::LINK_STATUS, &mut success);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function `GetShaderiv` is unsafe and requires unsafe block
--> src\main.rs:96:5
|
96 | gl::GetShaderiv(fragShader, gl::INFO_LOG_LENGTH, &mut len);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function `GetShaderInfoLog` is unsafe and requires unsafe block
--> src\main.rs:98:5
|
98 | / gl::GetShaderInfoLog(fragShader, len, ptr::null_mut(),
99 | | infoLog.as_mut_ptr() as *mut GLchar);
| |__________________________________________^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function `DeleteShader` is unsafe and requires unsafe block
--> src\main.rs:104:3
|
104 | gl::DeleteShader(vertexShader);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function `DeleteShader` is unsafe and requires unsafe block
--> src\main.rs:105:3
|
105 | gl::DeleteShader(fragShader);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
For more information about this error, try `rustc --explain E0133`.
error: could not compile `opengl-render-rs` (bin "opengl-render-rs") due to 23 previous errors
GPUと通信する必要があるドライバだという事は理解しています。
然し、グラフィックス関連のコードが全て「unsafe」になるなら、ゲーム開発でRustを使う意味は何なのでしょうか?
もう一つの苛立ちポイントは、Rustコンパイラの文法警察的な振る舞いです。
> cargo build
Compiling opengl-render-rs v0.1.0 (C:\Users\suwako\dev\stub\opengl-render-rs)
warning: variable `vertexShader` should have a snake case name
--> src\main.rs:54:9
|
54 | let vertexShader = gl::CreateShader(gl::VERTEX_SHADER);
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `vertex_shader`
|
= note: `#[warn(non_snake_case)]` (part of `#[warn(nonstandard_style)]`) on by default
warning: variable `cVtx` should have a snake case name
--> src\main.rs:55:9
|
55 | let cVtx = CString::new(VERTEX_SRC.as_bytes())
| ^^^^ help: convert the identifier to snake case: `c_vtx`
warning: variable `infoLog` should have a snake case name
--> src\main.rs:65:15
|
65 | let mut infoLog = vec![0u8; len as usize];
| ^^^^^^^ help: convert the identifier to snake case: `info_log`
warning: variable `fragShader` should have a snake case name
--> src\main.rs:72:9
|
72 | let fragShader = gl::CreateShader(gl::FRAGMENT_SHADER);
| ^^^^^^^^^^ help: convert the identifier to snake case: `frag_shader`
warning: variable `cFrag` should have a snake case name
--> src\main.rs:73:9
|
73 | let cFrag = CString::new(FRAG_SRC.as_bytes())
| ^^^^^ help: convert the identifier to snake case: `c_frag`
warning: variable `infoLog` should have a snake case name
--> src\main.rs:82:15
|
82 | let mut infoLog = vec![0u8; len as usize];
| ^^^^^^^ help: convert the identifier to snake case: `info_log`
warning: variable `shaderProgram` should have a snake case name
--> src\main.rs:89:9
|
89 | let shaderProgram = gl::CreateProgram();
| ^^^^^^^^^^^^^ help: convert the identifier to snake case: `shader_program`
warning: variable `infoLog` should have a snake case name
--> src\main.rs:98:15
|
98 | let mut infoLog = vec![0u8; len as usize];
| ^^^^^^^ help: convert the identifier to snake case: `info_log`
warning: `opengl-render-rs` (bin "opengl-render-rs") generated 8 warnings
Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.87s
変数の宣言方法くらい、大人として自分で決めさせてくれ!
最終的なコード:
extern crate glfw;
extern crate gl;
use glfw::{Action, Context, Key};
use gl::types::*;
use std::ffi::CString;
use std::ptr;
const VERTEX_SRC: &str = r#"
#version 460 core
layout (location = 0) in vec3 aPos;
void main() {
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}
"#;
const FRAG_SRC: &str = r#"
#version 460 core
out vec4 FragColor;
void main() {
FragColor = vec4(1.f, .5f, .2f, 1.f);
}
"#;
fn main() {
use glfw::fail_on_errors;
let mut glfw = glfw::init(glfw::fail_on_errors!()).unwrap();
glfw.window_hint(glfw::WindowHint::ContextVersionMajor(4));
glfw.window_hint(glfw::WindowHint::ContextVersionMinor(6));
glfw.window_hint(glfw::WindowHint::OpenGlProfile(glfw::OpenGlProfileHint::Core));
let (mut window, events) =
glfw.create_window(800, 600, "OpenGLレンダー", glfw::WindowMode::Windowed)
.expect("GLFWウィンドウを作成に失敗。");
window.make_current();
gl::load_with(|s| {
window.get_proc_address(s)
.map_or(std::ptr::null(), |f| f as *const _)
});
window.set_key_polling(true);
window.set_framebuffer_size_callback(|_wnd, w, h| {
unsafe { gl::Viewport(0, 0, w, h); }
});
let mut shaderProgram = 0;
unsafe {
let vertexShader = gl::CreateShader(gl::VERTEX_SHADER);
let cVtx = CString::new(VERTEX_SRC.as_bytes())
.expect("頂点シェーダー向けCStringに変換に失敗。");
gl::ShaderSource(vertexShader, 1, &cVtx.as_ptr(), ptr::null());
gl::CompileShader(vertexShader);
let mut success = gl::FALSE as GLint;
gl::GetShaderiv(vertexShader, gl::COMPILE_STATUS, &mut success);
if success == gl::FALSE as GLint {
let mut len = 0;
gl::GetShaderiv(vertexShader, gl::INFO_LOG_LENGTH, &mut len);
let mut infoLog = vec![0u8; len as usize];
gl::GetShaderInfoLog(vertexShader, len, ptr::null_mut(),
infoLog.as_mut_ptr() as *mut GLchar);
let log = String::from_utf8_lossy(&infoLog);
panic!("頂点シェーダーコンパイルに失敗。\n{}", log);
}
let fragShader = gl::CreateShader(gl::FRAGMENT_SHADER);
let cFrag = CString::new(FRAG_SRC.as_bytes())
.expect("フラグメントシェーダー向けCStringに変換に失敗。");
gl::ShaderSource(fragShader, 1, &cFrag.as_ptr(), ptr::null());
gl::CompileShader(fragShader);
gl::GetShaderiv(fragShader, gl::COMPILE_STATUS, &mut success);
if success == gl::FALSE as GLint {
let mut len = 0;
gl::GetShaderiv(fragShader, gl::INFO_LOG_LENGTH, &mut len);
let mut infoLog = vec![0u8; len as usize];
gl::GetShaderInfoLog(fragShader, len, ptr::null_mut(),
infoLog.as_mut_ptr() as *mut GLchar);
let log = String::from_utf8_lossy(&infoLog);
panic!("フラグメントシェーダーコンパイルに失敗。\n{}", log);
}
shaderProgram = gl::CreateProgram();
gl::AttachShader(shaderProgram, vertexShader);
gl::AttachShader(shaderProgram, fragShader);
gl::LinkProgram(shaderProgram);
gl::GetProgramiv(shaderProgram, gl::LINK_STATUS, &mut success);
if success == gl::FALSE as GLint {
let mut len = 0;
gl::GetShaderiv(fragShader, gl::INFO_LOG_LENGTH, &mut len);
let mut infoLog = vec![0u8; len as usize];
gl::GetShaderInfoLog(fragShader, len, ptr::null_mut(),
infoLog.as_mut_ptr() as *mut GLchar);
let log = String::from_utf8_lossy(&infoLog);
panic!("プログラムを受け取るに失敗。\n{}", log);
}
gl::DeleteShader(vertexShader);
gl::DeleteShader(fragShader);
}
const VERTICES: [f32; 12] = [
0.5, 0.5, 0.0,
0.5, -0.5, 0.0,
-0.5, -0.5, 0.0,
-0.5, 0.5, 0.0,
];
const INDICES: [u32; 6] = [
0, 1, 3,
1, 2, 3,
];
let mut VAO = 0;
let mut VBO = 0;
let mut EBO = 0;
unsafe {
gl::GenVertexArrays(1, &mut VAO);
gl::GenBuffers(1, &mut VBO);
gl::GenBuffers(1, &mut EBO);
gl::BindVertexArray(VAO);
gl::BindBuffer(gl::ARRAY_BUFFER, VBO);
gl::BufferData(gl::ARRAY_BUFFER, std::mem::size_of_val(&VERTICES) as isize,
VERTICES.as_ptr() as *const _, gl::STATIC_DRAW);
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, EBO);
gl::BufferData(gl::ELEMENT_ARRAY_BUFFER, std::mem::size_of_val(&INDICES) as isize,
INDICES.as_ptr() as *const _, gl::STATIC_DRAW);
gl::VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE,
3 * std::mem::size_of::<f32>() as GLint, std::ptr::null());
gl::EnableVertexAttribArray(0);
gl::BindBuffer(gl::ARRAY_BUFFER, 0);
gl::BindVertexArray(0);
}
while !window.should_close() {
unsafe {
gl::ClearColor(0.6, 0.1, 0.6, 1.0);
gl::Clear(gl::COLOR_BUFFER_BIT);
gl::UseProgram(shaderProgram);
gl::BindVertexArray(VAO);
gl::DrawElements(gl::TRIANGLES, INDICES.len() as GLint, gl::UNSIGNED_INT,
std::ptr::null());
}
window.swap_buffers();
glfw.poll_events();
for (_, event) in glfw::flush_messages(&events) {
match event {
glfw::WindowEvent::Key(Key::Q, _, Action::Press, _) => {
window.set_should_close(true)
},
_ => {},
}
}
}
}
コンパイラの警告が沢山出ましたが、只の意見なので修正しませんでした。
> cargo build
Compiling opengl-render-rs v0.1.0 (C:\Users\suwako\dev\stub\opengl-render-rs)
warning: value assigned to `shaderProgram` is never read
--> src\main.rs:53:27
|
53 | let mut shaderProgram = 0;
| ^
|
= help: maybe it is overwritten before being read?
= note: `#[warn(unused_assignments)]` (part of `#[warn(unused)]`) on by default
warning: variable `shaderProgram` should have a snake case name
--> src\main.rs:53:11
|
53 | let mut shaderProgram = 0;
| ^^^^^^^^^^^^^ help: convert the identifier to snake case: `shader_program`
|
= note: `#[warn(non_snake_case)]` (part of `#[warn(nonstandard_style)]`) on by default
warning: variable `vertexShader` should have a snake case name
--> src\main.rs:56:9
|
56 | let vertexShader = gl::CreateShader(gl::VERTEX_SHADER);
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `vertex_shader`
warning: variable `cVtx` should have a snake case name
--> src\main.rs:57:9
|
57 | let cVtx = CString::new(VERTEX_SRC.as_bytes())
| ^^^^ help: convert the identifier to snake case: `c_vtx`
warning: variable `infoLog` should have a snake case name
--> src\main.rs:67:15
|
67 | let mut infoLog = vec![0u8; len as usize];
| ^^^^^^^ help: convert the identifier to snake case: `info_log`
warning: variable `fragShader` should have a snake case name
--> src\main.rs:74:9
|
74 | let fragShader = gl::CreateShader(gl::FRAGMENT_SHADER);
| ^^^^^^^^^^ help: convert the identifier to snake case: `frag_shader`
warning: variable `cFrag` should have a snake case name
--> src\main.rs:75:9
|
75 | let cFrag = CString::new(FRAG_SRC.as_bytes())
| ^^^^^ help: convert the identifier to snake case: `c_frag`
warning: variable `infoLog` should have a snake case name
--> src\main.rs:84:15
|
84 | let mut infoLog = vec![0u8; len as usize];
| ^^^^^^^ help: convert the identifier to snake case: `info_log`
warning: variable `infoLog` should have a snake case name
--> src\main.rs:100:15
|
100 | let mut infoLog = vec![0u8; len as usize];
| ^^^^^^^ help: convert the identifier to snake case: `info_log`
warning: variable `VAO` should have a snake case name
--> src\main.rs:123:11
|
123 | let mut VAO = 0;
| ^^^ help: convert the identifier to snake case: `vao`
warning: variable `VBO` should have a snake case name
--> src\main.rs:124:11
|
124 | let mut VBO = 0;
| ^^^ help: convert the identifier to snake case: `vbo`
warning: variable `EBO` should have a snake case name
--> src\main.rs:125:11
|
125 | let mut EBO = 0;
| ^^^ help: convert the identifier to snake case: `ebo`
warning: `opengl-render-rs` (bin "opengl-render-rs") generated 12 warnings
Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.78s
> .\target\debug\opengl-render-rs.exe
総評
正直に言うと、OpenGLレンダラーはどの言語でも大体同じ様な物なので、特に複雑なプロジェクトではありません。
其れでも、初めてRustを使ってみた感想を述べます。
良い点:
- 依存関係がインストール済みなら、驚くほど速いコンパイル時間
- デフォルトで静的リンク(少なくともWindowsでは)
- コンパイラが問題点と解決方法を明確に教えてくれる
- 何処にも明示的な解放処理がなく、高レベル言語の様に感じる
- エラーメッセージをインラインで定義出来るのが新鮮
悪い点:
- コンパイラのインストール方法が非常に危険
- Node.jsやPython並みの巨大な依存関係地獄
- 依存関係がまだインストールされていない場合、コンパイルが永遠にかかる
- グラフィックスプログラミングでは「安全なコード」より「不安全なコード」の方が多い
- どうでも良い事(変数名の綴り方等)で大量の警告が出る
- 行が非常に冗長に成り易く、88文字以内に収める為に複数行に分割する必要がある
全体として、Rustを使っても「より安全なコードを書けている」という実感は殆どありませんでした。
コンパイラが多くの問題の解決策を教えてくれるのは良いですが、警告の多くは的外れです。
そして前述の通り、OpenGLは高レベルライブラリではなくGPUに直接話しかけるドライバーなので、unsafeブロックが多くなるのは当然です。
もう一つ気づいた点として、他の多くの言語では変数はデフォルトで変更可能であり、constを付けて定数にするのに対し、Rustでは変数はデフォルトで定数であり、mutを付けて可変にする必要があります。此れは良いとも悪いとも言えず、只気づいた事です。
あたしにとってRustは、システムプログラミングにおけるJavaScriptの様な存在に感じられました。
其れはコミュニティの声の大きい部分にも表れています。
RustでOpenGLレンダラーを作ったからといって、整形手術を受けたりホルモン治療を始めたり、LGBTフラッグを振ったりするつもりはありません。
あたしは今も誇り高いCとC++開発者です。
偶には反対側を試してみる事で、他の言語が存在する理由や、他の人が其れを好む理由を理解出来るのです。
次回はZigをもう一度試してみます。
少し変な感じになります。
何故なら以前既にZigを使った事があるからです。
但し其の時はZig 0.11.0で、現在はZig 0.16.0のリリースがかなり近づいています。
恐らく其のバージョンを使う事になるでしょう。
記事を書く頃には未だ開発中かもしんが。
以上