You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

macOS下Rust通过libloading动态加载的.dylib无法访问主进程运行时设置的环境变量(Windows正常)

macOS下Rust通过libloading动态加载的.dylib无法访问主进程运行时设置的环境变量(Windows正常)

嗨,我之前也踩过这个macOS动态库环境变量的坑,太懂这种跨平台不一致的糟心了!先帮你理清楚问题所在,再给你几个实用的解决办法。

先确认下你的问题场景和代码

首先还原下你的情况:

  • Windows上一切正常:Rust主进程运行时设的环境变量,动态加载的.dll能妥妥读到
  • 到了macOS就拉胯:同样的逻辑,用libloading加载Go编译的.dylib,不管是库初始化的init函数,还是调用sayHello的时候,都读不到Rust刚设的TEST_ENV值,输出全是空的

先把你提供的关键代码贴出来(方便大家对照排查):

Go侧生成.dylib的代码

package main

import "C"
import (
    "fmt"
    "os"
)

//export sayHello
func sayHello() {
    testEnv := os.Getenv("TEST_ENV")
    fmt.Println("sayHello testEnv:", testEnv)
}

func init() {
    testEnv := os.Getenv("TEST_ENV")
    fmt.Println("init testEnv:", testEnv)
}

func main() {
}

编译命令

GOOS=darwin GOARCH=arm64 CGO_ENABLED=1 go build -buildmode=c-shared -o skd.dylib

Rust侧调用代码

use libloading::Library;

fn main() {
    unsafe { std::env::set_var("TEST_ENV", "test123") };
    let lib = unsafe { Library::new("skd.dylib").unwrap() };
    let say_hello: libloading::Symbol<unsafe extern "C" fn()> = unsafe { lib.get(b"sayHello").unwrap() };
    unsafe { say_hello() };
}

实际运行结果

init testEnv: 
sayHello testEnv:

为啥macOS会这样?

这其实是macOS的dyld(动态链接器)和Windows的动态链接器在逻辑上的核心差异:

  • Windows那边,动态库加载时会直接用主进程当前的环境变量表,哪怕是加载前一秒刚设的变量都能读到
  • 但macOS的dyld在进程启动时就会拍一个环境变量的快照,之后不管主进程怎么改环境变量,动态库读取的都是这个冻结的快照。更坑的是,Go编译的c-shared库在加载瞬间就会跑init函数,这时候快照早就固定死了,后面Rust设的变量根本同步不进去,自然读不到。

给你几个解决办法

根据场景选就行:

1. 最稳妥:绕开环境变量,直接传参数(跨平台通用)

既然环境变量在macOS有这种坑,不如直接不用它,改成通过函数参数传值,Windows和macOS都能正常跑,还没兼容性问题。

  • 改Go侧的sayHello,让它接受参数:
//export sayHello
func sayHello(env *C.char) {
    testEnv := C.GoString(env)
    fmt.Println("sayHello testEnv:", testEnv)
}
  • Rust侧调用时直接把值传进去:
use libloading::Library;

fn main() {
    let test_env = "test123";
    let lib = unsafe { Library::new("skd.dylib").unwrap() };
    // 注意调整函数签名,匹配带参数的版本
    let say_hello: libloading::Symbol<unsafe extern "C" fn(*const i8)> = unsafe { lib.get(b"sayHello").unwrap() };
    unsafe { say_hello(test_env.as_ptr()) };
}

2. 凑合用:先设环境变量再启动进程

哦不对,Rust主进程里set_var之后再加载库还是没用,因为快照是进程启动时就固定的。如果一定要用环境变量,得在Rust进程启动前就把变量设好,或者用std::process::Command起一个子进程,在子进程里先设变量再加载库。但这个方案灵活性很低,适合环境变量固定不变的场景。

3. 不推荐:用私有API改快照(hack性质)

如果你死磕一定要用环境变量的方式,可以调用macOS的底层私有API去更新dyld的环境变量快照,但这个做法依赖系统内部实现,macOS一更新可能就崩了,而且全是unsafe代码,生产环境千万别用。

补充一句

你看到init函数也读不到值,是因为Go的c-shared库在加载的瞬间就会自动执行所有init函数,这时候dyld的快照早就冻结了,所以哪怕你在Rust里先设变量再加载库,init还是拿不到值,这和你看到的结果完全一致。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 13:04:35