我正在学习/试验Rust,在我发现这门语言的所有优雅之处中,有一个特点让我困惑,似乎完全不合适。

Rust在进行方法调用时自动解除对指针的引用。我做了一些测试来确定准确的行为:

struct X { val: i32 }
impl std::ops::Deref for X {
    type Target = i32;
    fn deref(&self) -> &i32 { &self.val }
}

trait M { fn m(self); }
impl M for i32   { fn m(self) { println!("i32::m()");  } }
impl M for X     { fn m(self) { println!("X::m()");    } }
impl M for &X    { fn m(self) { println!("&X::m()");   } }
impl M for &&X   { fn m(self) { println!("&&X::m()");  } }
impl M for &&&X  { fn m(self) { println!("&&&X::m()"); } }

trait RefM { fn refm(&self); }
impl RefM for i32  { fn refm(&self) { println!("i32::refm()");  } }
impl RefM for X    { fn refm(&self) { println!("X::refm()");    } }
impl RefM for &X   { fn refm(&self) { println!("&X::refm()");   } }
impl RefM for &&X  { fn refm(&self) { println!("&&X::refm()");  } }
impl RefM for &&&X { fn refm(&self) { println!("&&&X::refm()"); } }


struct Y { val: i32 }
impl std::ops::Deref for Y {
    type Target = i32;
    fn deref(&self) -> &i32 { &self.val }
}

struct Z { val: Y }
impl std::ops::Deref for Z {
    type Target = Y;
    fn deref(&self) -> &Y { &self.val }
}


#[derive(Clone, Copy)]
struct A;

impl M for    A { fn m(self) { println!("A::m()");    } }
impl M for &&&A { fn m(self) { println!("&&&A::m()"); } }

impl RefM for    A { fn refm(&self) { println!("A::refm()");    } }
impl RefM for &&&A { fn refm(&self) { println!("&&&A::refm()"); } }


fn main() {
    // I'll use @ to denote left side of the dot operator
    (*X{val:42}).m();        // i32::m()    , Self == @
    X{val:42}.m();           // X::m()      , Self == @
    (&X{val:42}).m();        // &X::m()     , Self == @
    (&&X{val:42}).m();       // &&X::m()    , Self == @
    (&&&X{val:42}).m();      // &&&X:m()    , Self == @
    (&&&&X{val:42}).m();     // &&&X::m()   , Self == *@
    (&&&&&X{val:42}).m();    // &&&X::m()   , Self == **@
    println!("-------------------------");

    (*X{val:42}).refm();     // i32::refm() , Self == @
    X{val:42}.refm();        // X::refm()   , Self == @
    (&X{val:42}).refm();     // X::refm()   , Self == *@
    (&&X{val:42}).refm();    // &X::refm()  , Self == *@
    (&&&X{val:42}).refm();   // &&X::refm() , Self == *@
    (&&&&X{val:42}).refm();  // &&&X::refm(), Self == *@
    (&&&&&X{val:42}).refm(); // &&&X::refm(), Self == **@
    println!("-------------------------");

    Y{val:42}.refm();        // i32::refm() , Self == *@
    Z{val:Y{val:42}}.refm(); // i32::refm() , Self == **@
    println!("-------------------------");

    A.m();                   // A::m()      , Self == @
    // without the Copy trait, (&A).m() would be a compilation error:
    // cannot move out of borrowed content
    (&A).m();                // A::m()      , Self == *@
    (&&A).m();               // &&&A::m()   , Self == &@
    (&&&A).m();              // &&&A::m()   , Self == @
    A.refm();                // A::refm()   , Self == @
    (&A).refm();             // A::refm()   , Self == *@
    (&&A).refm();            // A::refm()   , Self == **@
    (&&&A).refm();           // &&&A::refm(), Self == @
}

(游乐场)

所以,看起来,或多或少:

The compiler will insert as many dereference operators as necessary to invoke a method. The compiler, when resolving methods declared using &self (call-by-reference): First tries calling for a single dereference of self Then tries calling for the exact type of self Then, tries inserting as many dereference operators as necessary for a match Methods declared using self (call-by-value) for type T behave as if they were declared using &self (call-by-reference) for type &T and called on the reference to whatever is on the left side of the dot operator. The above rules are first tried with raw built-in dereferencing, and if there's no match, the overload with Deref trait is used.

确切的自动解引用规则是什么?有人能给出这样一个设计决策的正式理由吗?

我发现他们在文档的前两章中定义语言的方法和方式特别有趣。所以我决定试一试,从“你好,世界!”开始。

顺便说一句,我是在Windows 7 x64上这么做的。

fn main() {
    println!("Hello, world!");
}

发布cargo build并在目标\调试中查看结果,我发现结果的.exe为3MB。经过一番搜索(cargo命令行标志的文档很难找到…)我找到了——release选项并创建了发布版本。令我惊讶的是,.exe的大小只变小了一点:2.99MB而不是3MB。

我的期望是系统编程语言会产生一些紧凑的东西。

谁能详细解释一下Rust编译的目的是什么,它是如何从一个3行程序生成如此巨大的图像的?它是否编译到虚拟机?是否有我错过的strip命令(发布版本中的调试信息?)?还有什么能让我们理解的吗?

从文档来看,还不清楚。在Java中,你可以像这样使用split方法:

"some string 123 ffd".split("123");

我想做一个Rust包,既包含可重用库(其中大部分程序是实现的),也包含使用它的可执行文件。

假设我没有混淆Rust模块系统中的任何语义,我的Cargo应该是什么?Toml文件长什么样?

我有一个值,我想存储这个值和一个引用 在我自己类型的价值中:

struct Thing {
    count: u32,
}

struct Combined<'a>(Thing, &'a u32);

fn make_combined<'a>() -> Combined<'a> {
    let thing = Thing { count: 42 };

    Combined(thing, &thing.count)
}

有时,我有一个值,我想存储这个值和它的引用 相同结构中的值:

struct Combined<'a>(Thing, &'a Thing);

fn make_combined<'a>() -> Combined<'a> {
    let thing = Thing::new();

    Combined(thing, &thing)
}

有时,我甚至不取值的参考,我得到 同样的错误:

struct Combined<'a>(Parent, Child<'a>);

fn make_combined<'a>() -> Combined<'a> {
    let parent = Parent::new();
    let child = parent.child();

    Combined(parent, child)
}

在每一种情况下,我都会得到一个值“does”的错误 活得不够长”。这个错误意味着什么?

我试图弄清楚如何匹配Rust中的字符串。

我最初尝试这样匹配,但我发现Rust不能隐式地从std::string:: string转换为&str。

fn main() {
    let stringthing = String::from("c");
    match stringthing {
        "a" => println!("0"),
        "b" => println!("1"),
        "c" => println!("2"),
    }
}

这有一个错误:

error[E0308]: mismatched types
 --> src/main.rs:4:9
  |
4 |         "a" => println!("0"),
  |         ^^^ expected struct `std::string::String`, found reference
  |
  = note: expected type `std::string::String`
             found type `&'static str`

然后我尝试构造新的String对象,因为我找不到一个函数来将String转换为&str。

fn main() {
    let stringthing = String::from("c");
    match stringthing {
        String::from("a") => println!("0"),
        String::from("b") => println!("1"),
        String::from("c") => println!("2"),
    }
}

这给了我以下错误3次:

error[E0164]: `String::from` does not name a tuple variant or a tuple struct
 --> src/main.rs:4:9
  |
4 |         String::from("a") => return 0,
  |         ^^^^^^^^^^^^^^^^^ not a tuple variant or struct

如何在Rust中匹配字符串?

据我所知,引用/指针别名会阻碍编译器生成优化代码的能力,因为它们必须确保在两个引用/指针确实别名的情况下生成的二进制行为正确。例如,在下面的C代码中,

void adds(int *a, int *b) {
    *a += *b;
    *a += *b;
}

当clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)编译时,它会发出-O3标志

0000000000000000 <adds>:
   0:    8b 07                    mov    (%rdi),%eax  # load a into EAX
   2:    03 06                    add    (%rsi),%eax  # load-and-add b
   4:    89 07                    mov    %eax,(%rdi)  # store into a
   6:    03 06                    add    (%rsi),%eax  # load-and-add b again
   8:    89 07                    mov    %eax,(%rdi)  # store into a again
   a:    c3                       retq

这里代码存储回(%rdi)两次,以防int *a和int *b别名。

当我们显式地告诉编译器这两个指针不能与restrict关键字别名:

void adds(int *restrict a, int *restrict b) {
    *a += *b;
    *a += *b;
}

然后Clang将发出一个更优化的版本,有效地执行*a += 2 * (*b),如果(正如restrict所承诺的那样)*b没有通过赋值给*a而被修改,则等价:

0000000000000000 <adds>:
   0:    8b 06                    mov    (%rsi),%eax   # load b once
   2:    01 c0                    add    %eax,%eax     # double it
   4:    01 07                    add    %eax,(%rdi)   # *a += 2 * (*b)
   6:    c3                       retq

由于Rust确保(除了不安全的代码)两个可变引用不能别名,我认为编译器应该能够发出更优化的代码版本。

当我测试下面的代码并使用-C opt-level=3——emit obj用rustc 1.35.0编译时,

#![crate_type = "staticlib"]
#[no_mangle]
fn adds(a: &mut i32, b: &mut i32) {
    *a += *b;
    *a += *b;
}

它生成:

0000000000000000 <adds>:
   0:    8b 07                    mov    (%rdi),%eax
   2:    03 06                    add    (%rsi),%eax
   4:    89 07                    mov    %eax,(%rdi)
   6:    03 06                    add    (%rsi),%eax
   8:    89 07                    mov    %eax,(%rdi)
   a:    c3                       retq

这没有利用a和b不能别名的保证。

这是因为当前Rust编译器仍在开发中,还没有结合别名分析来进行优化吗?

这是因为即使在安全的Rust中,a和b仍然有可能别名吗?

我有以下几点:

let mut my_number = 32.90;

如何打印my_number的类型?

使用type和type_of不起作用。有其他方法可以打印数字的类型吗?

注意:此问题包含1.0之前已弃用的代码!不过答案是正确的。

在Rust中将str转换为int,我可以这样做:

let my_int = from_str::<int>(my_str);

我知道如何将String转换为int的唯一方法是获取它的切片,然后使用from_str对它这样:

let my_int = from_str::<int>(my_string.as_slice());

是否有一种方法可以直接将字符串转换为int?

我正在做Rust by Example教程,其中有以下代码片段:

// Vec example
let vec1 = vec![1, 2, 3];
let vec2 = vec![4, 5, 6];

// `iter()` for vecs yields `&i32`. Destructure to `i32`.
println!("2 in vec1: {}", vec1.iter()     .any(|&x| x == 2));
// `into_iter()` for vecs yields `i32`. No destructuring required.
println!("2 in vec2: {}", vec2.into_iter().any(| x| x == 2));

// Array example
let array1 = [1, 2, 3];
let array2 = [4, 5, 6];

// `iter()` for arrays yields `&i32`.
println!("2 in array1: {}", array1.iter()     .any(|&x| x == 2));
// `into_iter()` for arrays unusually yields `&i32`.
println!("2 in array2: {}", array2.into_iter().any(|&x| x == 2));

我完全困惑了-对于Vec,从iter返回的迭代器产生引用,从into_iter返回的迭代器产生值,但对于数组,这些迭代器是相同的?

这两个方法的用例/API是什么?