PATH や HOME、社内ツールが設定する MY_TOOL_HOME のような環境変数を読みたい場面はよくあります。フロントエンドは WebView 上の JavaScript なので process.env は存在せず、tauri-plugin-os にも環境変数を返す関数はありません。Rust 側で std::env を使うコマンドを作り、必要な変数だけをフロントへ渡すのが正攻法です。設定側の set_var は Rust 2024 エディションで unsafe になったので、その扱いも説明します。
前提条件
プラグインと追加権限は不要です。.env を読む場合だけ src-tauri で次を実行します。
cargo add dotenvy
1. フロントエンドから実装する (TypeScript)
import { invoke } from '@tauri-apps/api/core';
// 1 つ取得(未設定なら null)
const home = await invoke<string | null>('get_env', { name: 'HOME' });
// プレフィックスで絞って一覧取得
const mine = await invoke<Record<string, string>>('list_env', { prefix: 'MYAPP_' });
console.log(mine); // { MYAPP_MODE: "debug" }
// 現在のプロセス内だけに設定
await invoke('set_env', { name: 'MYAPP_MODE', value: 'debug' });
2. バックエンドから実装する (Rust)
src-tauri/src/lib.rs:
use std::collections::HashMap;
use std::env;
#[tauri::command]
fn get_env(name: String) -> Option<String> {
// 未設定 (NotPresent) も UTF-8 でない値 (NotUnicode) も None にまとめる
env::var(name).ok()
}
#[tauri::command]
fn list_env(prefix: String) -> HashMap<String, String> {
env::vars().filter(|(k, _)| k.starts_with(&prefix)).collect()
}
#[tauri::command]
fn set_env(name: String, value: String) -> Result<(), String> {
if name.is_empty() || name.contains('=') || name.contains('\0') {
return Err("invalid variable name".into());
}
// Rust 2024 では unsafe。他スレッドが環境を読み書きしていない前提が必要
unsafe { env::set_var(&name, &value) };
Ok(())
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
let _ = dotenvy::dotenv(); // .env は起動直後、スレッドが増える前に読む。無ければ何もしない
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![get_env, list_env, set_env])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
set_var は現在のプロセスと、この後に起動する子プロセスにだけ効きます。OS のユーザー環境変数を書き換えるものではなく、再起動すれば消えます。unsafe ブロックは 2021 エディションでも書けるので、edition に関係なくこの形にしておきます。
設定は起動前に、子プロセスには直接渡す
Tauri アプリは起動時点で非同期ランタイムや WebView のスレッドが動いており、set_var を安全に呼べる「単一スレッド」の条件は厳密には満たせません。macOS / Linux では、run() の先頭で tauri::Builder を組み立てる前に一度だけ設定するか、子プロセス向けなら set_var を使わず std::process::Command::env() や shell プラグインの env オプションでそのプロセスにだけ渡します。
.env とビルド時の値
dotenvy::dotenv() はカレントディレクトリから上に向かって .env を探します。npm run tauri dev では src-tauri/.env が読まれます。本番ビルドに .env は同梱されないので、配布後に必要な値はビルド時に埋め込むか(option_env!("MYAPP_API_BASE"))、設定ファイルに保存します。フロント側で使う定数は Vite の VITE_ プレフィックス付き変数を import.meta.env で参照する方が簡単です。
動作確認
MYAPP_MODE=debug npm run tauri dev # macOS / Linux
$env:MYAPP_MODE = "debug"; npm run tauri dev # Windows PowerShell
コンソールで await invoke('list_env', { prefix: 'MYAPP_' }) を実行すると { MYAPP_MODE: "debug" } が返ります。set_env で別の値を入れた直後に get_env を呼ぶと新しい値が返り、再起動すると元に戻ります。
よくあるエラーと対処法
ReferenceError: process is not defined: フロントでprocess.env.Xを書いています。WebView に Node.js のprocessはありません。コマンド経由にするか、import.meta.env.VITE_Xに置き換えます。call to unsafe function is unsafe and requires unsafe block(E0133): Rust 2024 でset_varをunsafeなしで呼んでいます。unsafe { env::set_var(...) }に直します。get_envが常にnull: 変数名の大文字小文字(Linux / macOS は区別する)か、GUI から起動したためシェルの環境を引き継いでいないことが原因です。後述の OS ごとの違いを確認してください。
OS ごとの違いと注意点
- Windows: 変数名は大文字小文字を区別しません。ホームは
HOMEではなくUSERPROFILE、ユーザー名はUSERNAMEです。set_varはマルチスレッドでも安全とされています。 - macOS: Finder や Dock から起動したアプリは launchd の最小限の環境しか持たず、
.zshrcで設定したPATHや独自変数は見えません。「ターミナルからopenで起動したときだけ動く」現象の原因はこれです。 - Linux:
.desktopから起動した場合も同様にシェルの環境を引き継ぎません。set_var/remove_varは他スレッドがgetenvしている最中だと未定義動作になるため、起動直後以外では避けます。 - 値の扱い: 環境変数にはトークンや DB パスワードが入っていることがあります。
list_envをプレフィックス無しで公開すると、フロントの XSS から丸ごと抜かれます。必ず許可リストかプレフィックスで絞ってください。
