【问题标题】:No method named X found for type *mut Y in the current scope在当前范围内找不到类型 *mut Y 的名为 X 的方法
【发布时间】:2017-03-22 05:48:30
【问题描述】:

我想在结构I 上调用方法a。我被告知 Rust 找不到该方法,但不确定原因:

error: no method named `a` found for type `*mut I` in the current scope

 --> src/lib.rs:7:16
  |
7 |     unsafe { i.a(5) }
  |                ^

这是一个最小的可重现示例:

extern crate libc;
use self::libc::int32_t;

#[no_mangle]
pub extern "C" fn i_a(i: *mut I) -> *mut int32_t {
    unsafe { i.a(5) } // error: no method named `a` found for type `*mut I` in the current scope
}

#[derive(Debug, PartialEq)]
pub struct I {
    pub values: Vec<i32>,
}

impl I {
    pub fn a(&self, n: i32) -> i32 {
        return 0;
    }
}

我该如何解决这个问题?

【问题讨论】:

  • 解引用指针,不像引用,不是隐式的。您必须取消引用该指针。
  • 旁注:您的i_a 函数返回*mut int32_ta 方法返回i32,即使您解决了问题,类型也不会匹配。

标签: rust


【解决方案1】:

我们把extern C的东西去掉,这里没用:

#[derive(Debug, PartialEq)]
pub struct I {
    pub values: Vec<i32>,
}

impl I {
    pub fn a(&self, n: i32) -> i32 {
        return 0;
    }
}

pub fn i_a(i: *mut I) -> i32 {
    unsafe { i.a(5) }
}

问题在于 Rust 中的指针非常受限:它们只有在 the primitive pointer type 上实现的方法。

基本上,您可以检查指针是否为空,比较它是否相等,执行一些算术运算并取消引用它。请注意,指针上实际上很少有操作是不安全的(主要是算术和取消引用)。

要真正使用pointee,首先需要解引用指针以从中获取引用;这是不安全的,但使用引用是安全的。

因此,您可以将i_a 重写为:

pub fn i_a(i: *mut I) -> i32 {
    unsafe { (*i).a(5) }
}

或:

pub fn i_a(i: *mut I) -> i32 {
    unsafe { &*i }.a(5)
}

然后它就会起作用。

【讨论】:

  • 我希望人们盲目地复制粘贴您的代码,所以我建议添加 NULL 检查。
  • @Shepmaster:好吧,如果指针为空,那么它可能会崩溃;如果你传递任何野指针,这就是你会得到的行为。
  • 这种反应对你来说似乎是不寻常的laissez faire。按照这种逻辑到荒谬的结局,Rust 所做的许多(全部?)检查似乎都是无关紧要的,因为如果你“错误”地做它,它无论如何都可能崩溃。
  • @Shepmaster:只是我看到太多新人认为他们的代码是安全的,因为他们有一个空指针检查,然后想知道为什么调试器将0x4作为他们的地址对象(C++)。在处理unsafe 代码时,我宁愿人们坐下来仔细考虑:他们可以在这里有一个空指针吗?如果可以,响应应该是什么(忽略/崩溃/其他)?如果他们不能,也许assert(或debug_assert)应该记录这个?
  • 我认为我们不应该检查nullptr,除非界面明确将其列为一种可能性或表达Option::None的方式。
【解决方案2】:

我想在结构 I 上调用方法 a

代码确实没有有一个struct I;它有一个 可变指针 指向结构 I

强烈建议在与他们进行任何深入合作之前阅读chapter on raw pointers。 Rust 是一门很棒的语言,当您使用安全方面时,编译器会非常努力地防止出错;当你进入不安全状态时,需要知道你在做什么,因为你已经告诉编译器退后观望。

引用和原始指针之间的一个具体区别是原始指针可能是NULL。另一方面,引用可能永远不会是NULL。这就是每次取消引用原始指针时都需要使用unsafe 块的原因之一。

通常,您 check for a NULL pointer 然后将其转换为参考,如果它不是 NULL

let i = &*i;     // For `&T`
let i = &mut *i; // For `&mut T`

既然有了引用,就可以调用方法了。

还有像as_ref这样的辅助函数:

pub extern "C" fn i_a(i: *mut I) -> int32_t {
    match unsafe { i.as_ref() } {
        Some(i) => i.a(5),
        None => 42,
    }
}

该代码还有一个巨大的漏洞等待被利用。 i_a 声称返回一个指向整数的指针,但代码直接返回一个整数。这是一个非常糟糕的不匹配

【讨论】:

    【解决方案3】:

    方法a 在技术上采用self: &amp;I 参数(但简写&amp;self 更方便)。因此,该方法需要引用I。变量i 的类型为*mut I,但应为&amp;I。这些类型显然不匹配。但是您可以通过取消引用原始指针并立即再次引用它((&amp;*i).a(42))轻松地将您的i 转换为&amp;I。或者,您可以实现指针的方法:

    trait A {
        fn a(self, n: i32) -> i32;
    }
    
    impl A for *mut I {
        fn a(self, n: i32) -> i32 {
            return 0;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2019-11-16
      • 2023-01-15
      • 2017-03-16
      • 2018-07-08
      • 2021-05-13
      • 1970-01-01
      • 1970-01-01
      • 2021-09-24
      • 2021-09-21
      相关资源
      最近更新 更多