< 返回版块

sankred9527 发表于 2024-03-19 06:27

譬如,在 nginx 的C源码里,下面的 void *ctx 代码非常常见。 因为 context的类型可能千奇百怪,无法预测,只能用一个 void *来表示。 linux kernel 里也是大量的类似代码。 请问: rust里该如何正确的做呢?

struct ngx_module_s {
    ngx_uint_t            ctx_index;
    ngx_uint_t            index;
    char                 *name;
    ngx_uint_t            spare0;
    ngx_uint_t            spare1;
    ngx_uint_t            version;
    const char           *signature;
    void                 *ctx;
    ngx_command_t        *commands;
    ngx_uint_t            type;
}

评论区

写评论
regomne 2024-03-25 11:12

C使用 void* 是因为C的表达能力被限制了,没有更好的方法……C++通过带虚函数的class(本质上就是约定了一个事先指定方法的void*,也就是this指针)来做到C中void*的事情。Rust中就是trait。当然你要ffi的话那还是只能用c_void。

mu2019 2024-03-21 13:54

如果不是明确的类型,rust比较难。 void *就是c/c++的不安全根本来源。

Reiase 2024-03-19 22:07

插件是不是定义个trait就可以了,不需要感知插件内部的数据类型。如果是为了用ctx传参的话,可以试试换成scope传参(参考这里:https://github.com/reiase/hyperparameter/tree/master/core/examples):

fn foo() {
    with_params! {
        get param1 = example.param1 or 0; // 读取参数,或者使用默认值0

        println!("param1={}", param1);
    }
}

foo();  // 输出param1=0,没有参数scope,
with_params! { // 开始一个参数scope
    set example.param1= 1; // 设定参数scope内的参数

     foo(); // 输出param1=1
}
foo();  // 输出param1=0,参数scope结束,参数失效

--
👇
sankred9527:

https://doc.rust-lang.org/stable/std/task/struct.RawWaker.html

譬如这个模块。又或者你自己做个支持plugin的系统,你没法预料plugin的作者用的数据类型,怎么用enum 呢?

--
👇
jiacai2050: IMO,void * 算是 C 里面的“糟粕”了,Rust 里完全有更好的做法,如果你想表示的类型比较多,而且可以用一个 enum 来表示,而不是想 C 里面对 void * 强转。

TinusgragLin 2024-03-19 14:47

楼主可以想一下 interface 这个概念,这层抽象就是为了不用考虑具体类型,只要这些类型有共有的行为即可,极端一点这个共有的行为可以是输出一个代表类型的编码,那这层抽象就可以囊括所有类型了,具体实现上楼主可以搜一搜 vtable 这个概念,我记得是从 C++ 的虚函数表的实现中传过来的。

TinusgragLin 2024-03-19 14:29

我自己会把用 void * 看作在 C 中做类型抹除,和指针类型转换一起为 C 赋予了动态类型语言的灵活性。在 Rust 中我记得通常用 &dyn Trait(极端一点就是 &dyn Any) 来做类型抹除。

Bai-Jinlin 2024-03-19 13:13

手动使用RawWaker构建Waker的函数是unsafe,RawWakerVTable里的函数也全是unsafe,安不安全取决于你写的代码如何编写的。https://doc.rust-lang.org/stable/src/alloc/task.rs.html#108 这么写就是安全的。

--
👇
sankred9527: 我看了 RawWaker的代码了, 本质上和 void *差不多

请看 RawWaker 的这部分代码: https://doc.rust-lang.org/stable/src/core/task/wake.rs.html#99

请问, 如果按照RawWaker这样做,那本质上和C的 void * 一样不安全了?

--
👇
Bai-Jinlin: https://doc.rust-lang.org/stable/std/task/struct.RawWaker.html

作者 sankred9527 2024-03-19 12:56

你这样可以,但是体现不出来 rust 相对于 C 的 “优越性” , 没法装逼啊, 哈哈

--
👇
ManonLoki: use std::ffi::c_void;

#[repr(C)] sturct Context{ data:i32 }

extern "C" fn function_name(context: mut c_void) { // 在这里处理 ptr 转换为对应类型 let context: &Context = unsafe { &(context as *const Context) }; } 好像是这样

作者 sankred9527 2024-03-19 12:51

https://doc.rust-lang.org/stable/std/task/struct.RawWaker.html

譬如这个模块。又或者你自己做个支持plugin的系统,你没法预料plugin的作者用的数据类型,怎么用enum 呢?

--
👇
jiacai2050: IMO,void * 算是 C 里面的“糟粕”了,Rust 里完全有更好的做法,如果你想表示的类型比较多,而且可以用一个 enum 来表示,而不是想 C 里面对 void * 强转。

作者 sankred9527 2024-03-19 12:49

我看了 RawWaker的代码了, 本质上和 void *差不多

请看 RawWaker 的这部分代码: https://doc.rust-lang.org/stable/src/core/task/wake.rs.html#99

请问, 如果按照RawWaker这样做,那本质上和C的 void * 一样不安全了?

--
👇
Bai-Jinlin: https://doc.rust-lang.org/stable/std/task/struct.RawWaker.html

ManonLoki 2024-03-19 10:50

use std::ffi::c_void;

#[repr(C)] sturct Context{ data:i32 }

extern "C" fn function_name(context: mut c_void) { // 在这里处理 ptr 转换为对应类型 let context: &Context = unsafe { &(context as *const Context) }; } 好像是这样

bestgopher 2024-03-19 10:23

*const ()

jiacai2050 2024-03-19 09:01

IMO,void * 算是 C 里面的“糟粕”了,Rust 里完全有更好的做法,如果你想表示的类型比较多,而且可以用一个 enum 来表示,而不是想 C 里面对 void * 强转。

Bai-Jinlin 2024-03-19 08:29

https://doc.rust-lang.org/stable/std/task/struct.RawWaker.html

1 共 13 条评论, 1 页