我正在尝试实现一个用Rust编写的跨平台DLL/SharedObject。我在这个库中初始化了一个全局可变的哈希表。当我在这个哈希表中添加一个键和值时,键(&'static str
)被释放,用\0
字符重置,并重新分配给另一个用途。我不理解这种行为,因为我将变量声明为&'static str
,所以Rust在进程完成之前不应该释放内存。你能给我解释一下你的行为吗?你知道解决这个问题的正确方法吗?
我在可执行文件中没有这种行为,只有在DLL中。
代码Rust DLL/SharedObject:
use std::{sync::Mutex, collections::HashMap};
use lazy_static::lazy_static;
use std::io::{stdout, Write};
#[derive(Clone)]
enum Color {
Red,
}
impl Color {
fn value(&self) -> i32 {
match *self {
Color::Red => 1,
}
}
}
lazy_static! {
static ref STATE_OK: State = {
State {
name: String::from("OK"),
color: Color::Green,
character: "+".to_string(),
}
};
static ref STATES: Mutex<HashMap<&'static str, Box<dyn _State + Send>>> = {
let _states: HashMap<&'static str, Box<dyn _State + Send>> = HashMap::from(
[
(
"OK",
Box::new(STATE_OK.clone()) as Box<dyn _State + Send>
)
]
);
Mutex::new(_states)
};
}
fn rust_from_c_string (string_c: *const u8) -> &'static str {
let length = strlen(string_c);
let slice = unsafe { std::slice::from_raw_parts(string_c, length) };
std::str::from_utf8(slice).unwrap()
}
fn strlen(string_c: *const u8) -> usize {
let mut length = 0;
unsafe {
while *string_c.add(length) != 0 {
length += 1;
}
}
length
}
#[no_mangle]
pub extern "C" fn add_state (key_: *const u8, character_: *const u8, color_: *const u8) {
let key: &'static str = rust_from_c_string(key_);
let color = rust_from_c_string(color_);
let character = rust_from_c_string(character_);
let mut _states = STATES.lock().unwrap();
_states.insert(
key,
Box::new(State {
name: String::from(key),
color: match color {
"red" => Color::Red,
},
character: String::from(character),
}) as Box<dyn _State + Send>
);
}
#[no_mangle]
pub extern "C" fn messagef (text_: *const u8, state_: *const u8) {
let text = rust_from_c_string(text_);
let state = rust_from_c_string(state_);
let to_print: String;
let _states = STATES.lock().unwrap();
let state = _states.get(&*state.unwrap_or("OK").to_string()).unwrap();
print!("{}", to_print);
let _ = stdout().flush();
}
#[no_mangle]
pub extern "C" fn print_all_state () {
DEFAULT_STATE.lock().unwrap().print("not found");
let _states = STATES.lock().unwrap();
for (key, state) in _states.iter() {
state.print(key);
}
}
从Python中调用:
from ctypes import c_char_p, c_ubyte, c_ushort, pointer
from os.path import join, dirname, exists
from os import name, getcwd
if name == "nt":
from ctypes import windll as sysdll
filename = "TerminalMessages.dll"
else:
from ctypes import cdll as sysdll
filename = "libTerminalMessages.so"
filenames = (join(dirname(__file__), filename), join(getcwd(), filename))
for filename in filenames:
if exists(filename):
break
else:
raise FileNotFoundError(f"Library {filename!r} is missing")
lib = sysdll.LoadLibrary(filename)
lib.print_all_state()
lib.add_state(
c_char_p("TEST".encode()),
c_char_p("T".encode()),
c_char_p("red".lower().encode()),
)
lib.messagef(
c_char_p("test".encode()),
c_char_p("TEST".encode()),
)
lib.print_all_state()
完整的源代码是here。
我截图如下:
1.我添加了两个键和值,分别命名为TEST
和TEST2
- i列出了所有的键和值,
TEST
和TEST2
都有正确的键值
1.我使用我的库和哈希表与初始化的关键字,这是工作良好
1.我列出了所有键和值,TEST2
值与键\0\0\0\0\0
(5个\0
字符)一起出现,TEST
值与键Ques
(先前消息中使用的Question
单词的一部分)一起出现。
1.我使用我的库和哈希表与TEST
,TEST2
和初始化的关键字。未找到TEST
,TEST2
,但找到了初始化的密钥。
。
1条答案
按热度按时间amrnrhlw1#
你错误地处理了Python字符串:
c_char_p("TEST".encode())
没有'static
生命周期。我不理解这种行为,因为我将变量声明为
&'static str
,所以Rust在进程完成之前不应该释放内存。Rust无法控制从其他语言传递给它的内存,将其注解为
'static
不会改变任何东西。Rust的生命周期是 * 描述性的 *,而不是 * 规定性的 *,你对生命周期的描述是错误的。from_raw_parts
的部分安全性要求是推断的生存期不长于底层对象的生存期。如果你确实保证了Python字符串将无限期地持久化,那么这是一回事,但你没有。Rust没有释放内存,但Python是,用于字符串的内存将被GC回收并重用(或者显然在这种情况下用零清除),而Rust仍然有一个指针指向它所在的位置。
您必须通过创建一个 owned
String
然后leak()
-ing它来创建一个真正的&'static str
来保证这一点。大概是这样的:尽管在这一点上,您应该让您的
HashMap
从一开始就存储String
s(或至少Cow<str, 'static>
),以避免在您的API需要清除数据时泄漏。