陷阱


今天又看了一遍"The Rust Programming Language",没想到竟然跳进了一个坑里面。。
先说一个新的东西Loop labels

'outer: for x in 0..10 {
    'inner: for y in 0..10 {
        if x % 2 == 0 { continue 'outer; } // continues the loop over x
        if y % 2 == 0 { continue 'inner; } // continues the loop over y
        println!("x: {}, y: {}", x, y);
    }
}

感觉像是不是goto又像是goto的东西,更像是Ruby里的修饰,这个循环标签适合于循环中的continuebreak,总之这个东西能够在嵌套循环中减少些代码。

那么问题来了,怎样实现 let x = "a" + "b"呢? 懂一点C++的人不加思考的话会说运算符重载,当让他们写这个重载的时候,他们就无从下手了。
我今天就是跳进了这个坑里面。。。

use std::ops::Add;

impl<'a> Add<&'a str> for &'a str {
    fn add(self, other: &'a str) -> &str {
        let res = self.to_string() + other;
        res.as_str()
    }
}

fn main() {
    let (x, y) = ("this", "is");
    let z = x + y;

    assert_eq!("thisis", z);
}

多么简单的代码。但是,虽然不像C++那样无从下手,这个代码是通不过编译的!

t.rs:3:1: 8:2 error: the impl does not reference any types defined in this crate; only traits defined in the current crate can be implemented for arbitrary types [E0117]
t.rs:3 impl<'a> Add<&'a str> for &'a str {
t.rs:4     fn add(self, other: &'a str) -> &str {
t.rs:5         let res = self.to_string() + other;
t.rs:6         res.as_str()
t.rs:7     }
t.rs:8 }
t.rs:3:1: 8:2 help: run `rustc --explain E0117` to see a detailed explanation
error: aborting due to previous error

这个错误的解释(rustc --explain E0117)大概是说在这个crate里面既没有trait的原型也没有&str的定义,这在Rust中是禁止的!要么这样

use std::ops::Add;

#[derive(Debug)]
struct Foo {x:i32}

impl Add for Foo {
    type Output = i32;

    fn add(self, other: Foo) -> i32 {
        self.x + other.x
    }
}

要么这样

trait KK {
   fn p(self);
}

impl KK for String {
    fn p(self) { println!("{}", self);}
}

即,traittype至少有一个要在当前的crate
换句话说,要想直接实现let x = "a" + "b"是不可能了,但是可以通过一些改变来实现,那么这样行不行呢?

use std::ops::Add;

impl Add for &'static str {
    type Output = String;

    fn add(self, other: &'static str) -> String {
        let res = self.to_string() + other;
        res
    }
}

fn main() {
    let (x, y) = ("this", "is");
    let z = x + y;

    assert_eq!("thisis", z);
}

编译结果是这样

rs r.rs                                                                                  101 ↵
r.rs:3:1: 10:2 error: the impl does not reference any types defined in this crate; only traits defined in the current crate can be implemented for arbitrary types [E0117]
r.rs:3 impl Add for &'static str {
r.rs:4     type Output = String;
r.rs:5 
r.rs:6     fn add(self, other: &'static str) -> String {
r.rs:7         let res = self.to_string() + other;
r.rs:8         res
       ...
r.rs:3:1: 10:2 help: run `rustc --explain E0117` to see a detailed explanation
error: aborting due to previous error

同样的错误!!原因同样是

This error indicates a violation of one of Rust's orphan rules for trait
implementations. The rule prohibits any implementation of a foreign trait (a
trait defined in another crate) where

  • the type that is implementing the trait is foreign
  • all of the parameters being passed to the trait (if there are any) are also
    foreign.

    再看下面这个实现
angel@Chameleon  ~  cat t.rs
use std::ops::Add;

struct T(&'static str);

impl Add for T {
    type Output = String;

    fn add(self, other: T) -> String {
        let T(tmp1) = self;
        let T(tmp2) = other;
        let res = tmp1.to_string() + tmp2;
        res
    }
}

fn main() {
    let (x, y) = (T("this"), T("is"));
    let z = x + y;

    assert_eq!("thisis", z);
}
angel@Chameleon  ~ rs t.rs
angel@Chameleon  ~ ./t
angel@Chameleon  ~   

成功编译,并且成功运行。它只是遵守了T(&'static str)这个类型是在这个crate中,这一条规则!


以上只是针对两个本身就不支持Add的类型的实现。那么,重载原本就支持的会怎么样呢?
比如:

use std::ops::Add;

impl Add for i32 {
    type Output = i32;

    fn add(self, other: i32) -> i32 {
        self + other
    }
}

fn main() {
    let (x, y) = (1, 2);
    let z = x + y;

    assert_eq!(3, z);
}

下面是编译的输出:

angel@Chameleon  ~ rs r.rs           
r.rs:3:1: 9:2 error: the impl does not reference any types defined in this crate; only traits defined in the current crate can be implemented for arbitrary types [E0117]
r.rs:3 impl Add for i32 {
r.rs:4     type Output = i32;
r.rs:5 
r.rs:6     fn add(self, other: i32) -> i32 {
r.rs:7         self + other
r.rs:8     }
       ...
r.rs:3:1: 9:2 help: run `rustc --explain E0117` to see a detailed explanation
r.rs:3:1: 9:2 error: conflicting implementations for trait `core::ops::Add` [E0119]
r.rs:3 impl Add for i32 {
r.rs:4     type Output = i32;
r.rs:5 
r.rs:6     fn add(self, other: i32) -> i32 {
r.rs:7         self + other
r.rs:8     }
       ...
r.rs:3:1: 9:2 help: run `rustc --explain E0119` to see a detailed explanation
r.rs:3:1: 9:2 note: conflicting implementation in crate `core`
r.rs:3 impl Add for i32 {
r.rs:4     type Output = i32;
r.rs:5 
r.rs:6     fn add(self, other: i32) -> i32 {
r.rs:7         self + other
r.rs:8     }
       ...
error: aborting due to 2 previous errors

这比上面的还多出了一个错误,另外个错误说的是There are conflicting trait implementations for the same type.可以看出Rust是拒绝override的! 为什么我会尝试这个,是因为

angel@Chameleon  ~ cat overload.rb 
#!/usr/bin/ruby -w

class Fixnum
        def +(other)
                self - other
        end
end

print "2 + 1 = #{2 + 1}\n"

angel@Chameleon  ~ ruby overload.rb
overload.rb:4: warning: method redefined; discarding old +
2 + 1 = 1
angel@Chameleon  ~  

这在Ruby里面是可以的啊。。。



转载请注明:Serenity » 陷阱

上一篇