【发布时间】:2022-01-08 14:45:09
【问题描述】:
在以下情况下,我正试图围绕 Rust 的别名规则:
假设我们在 C 中有一个内存分配。我们将一个指向该分配的指针传递给 Rust。 Rust 函数对分配做一些事情,然后调用回 C 代码(没有任何参数),其中另一个 rust 函数被调用,其分配与参数相同。现在,我们假设只有第一个 Rust 函数获得可变引用。
调用堆栈如下所示:
Some C Code (owns data)
Rust(pointer as &mut)
Some C code (does not get parameters from Rust)
Rust(pointer as &)
作为一个简短的示例,让我们假设以下两个文件: 测试.c
#include <stdio.h>
#include <stdlib.h>
void first_rust_function(int * ints);
void another_rust_function(const int * ints);
int * arr;
void called_from_rust() {
another_rust_function(arr);
}
int main(int argc, char ** argv) {
arr = malloc(3*sizeof(int));
arr[0]=3;
arr[1]=4;
arr[2]=53;
first_rust_function(arr);
free(arr);
}
test.rs
use std::os::raw::c_int;
extern "C" { fn called_from_rust(); }
#[no_mangle]
pub extern "C" fn first_rust_function(ints : &mut [c_int;3]) {
ints[1] = 7;
unsafe { called_from_rust() };
}
#[no_mangle]
pub extern "C" fn another_rust_function(ints : &[c_int;3]) {
println!("Second value: {}", ints[1])
}
(为了完整起见:运行此代码会打印“第二个值:7”)
请注意,Rust (called_from_rust()) 对 C 的回调没有任何参数。因此,Rust 编译器没有任何人可能从指向的值中读取的信息。
我的直觉告诉我这是未定义的行为,但我不确定。
我快速浏览了 Stacked Borrows,发现违反了该模型。在上面的例子中,只有Rule (protector) 被破坏了,但是如果first_rust_function(ints : &mut [c_int;3]) 在调用called_from_rust() 之后仍然使用ints 也会违反其他规则。
但是,我没有找到任何官方文档说明 Stacked Borrows 是 Rust 编译器使用的别名模型,并且在 Stacked Borrows 下被认为未定义的所有内容实际上在 Rust 中都是未定义的。天真地,这看起来与将&mut 强制转换为& 非常相似,因此它实际上可能是理智的,但鉴于called_from_rust() 不将引用作为参数,我认为这种推理不适用。
这让我想到了实际的问题:
- 上述代码是否调用了未定义的行为(以及为什么/为什么不?)
- 如果未定义:如果
called_from_rust()将指针作为参数并将其向前传递:void called_from_rust(const int * i) { another_rust_function(i); },则行为是否明确? - 如果两个 Rust 函数都使用
&mut [c_int;3]会怎样?
【问题讨论】:
-
C 绑定应该包含
restrict用于绑定 rust 函数的任何可变引用参数。
标签: rust undefined-behavior ffi