< 返回版块

sstudioer 发表于 2021-03-04 00:28

c++ unique_ptr系列和rust的所有权差不多的; 至少在新项目中严格遵循RAII, 不会存在问题的;

rust: 边界检查, &是指针等等限制总是要绕, 解引用, unsafe...;

pub fn upper(str: &mut String){
	unsafe {
	        let chars = str.as_mut_vec();
		for c in  chars {
			if *c as u8 <= 'z' && *c as u8 >= 'a'{
				*c = *c - 32;
			}
		}
	}
}
  
     unsafe {
        map.get_unchecked(0)
     }
     if let val = map.get(0)
     map.get(0).unwarp()...
 
C++   
	// 大写
    void upper(string &str){
    	for(char & i : str){
		if (i <= 'z' && 'a' <= i) {
			i -= 32;
		}
    	}
    }
    map[0] 
    map[0] == NULL

评论区

写评论
eweca-d 2021-03-05 14:06

我只能认为或许优化的时候会把这几次位运算全都优化掉,所以一直懒得改吧?但无论如何,直接一次运算一次判断明显更容易懂,维护起来也更方便些吧?

eweca-d 2021-03-05 14:03

你这个应该是1.52改的,还没release。

    #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")]
    #[rustc_const_stable(feature = "const_ascii_methods_on_intrinsics", since = "1.52.0")]
    #[inline]
    pub const fn to_ascii_lowercase(&self) -> char {
        if self.is_ascii_uppercase() {
            (*self as u8).ascii_change_case_unchecked() as char
        } else {
            *self
        }
    }

我的1.51版本是,先转化为U8,然后转化为小写字母的U8,然后转为char。其中小写大写U8的转化是两次的位运算。还需要判断一次是否是小写。

    #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")]
    #[inline]
    pub fn to_ascii_lowercase(&self) -> char {
        if self.is_ascii() { (*self as u8).to_ascii_lowercase() as char } else { *self }
    }

    #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")]
    #[inline]
    pub fn to_ascii_lowercase(&self) -> u8 {
        // Set the fifth bit if this is an uppercase letter
        *self | ((self.is_ascii_uppercase() as u8) << 5)
    }

反过来,转为大写是三次位运算和一次判断。

    #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")]
    #[inline]
    pub fn to_ascii_uppercase(&self) -> u8 {
        // Unset the fifth bit if this is a lowercase letter
        *self & !((self.is_ascii_lowercase() as u8) << 5)
    }

估计1.52会一起改了吧。本来这个就有点奇怪,明明可以一次判断一次运算完成,硬要一次判断两/三次位运算完成。

--
👇
johnmave126: 我又查了一下标准库是一次判断,一次位运算啊

to_ascii_lowercase

ascii_change_case_unchecked

--
👇
eweca-d: 首先这里没有边界检查吧?如果你指的是检查小写字母边界的话,在rust里,使用的是b'a'这种形式就可以转化为ascii值了(u8)。另外吐槽下,根据C++反推,你的rust的upper和lower函数的ascii写反了。

解引用总要绕,这个不用绕属于语法糖范畴了吧。还可以吐槽C++解引用式使用变量需要->而rust有自动deref呢。

unsafe是特性。你既然想做危险操作,那就要unsafe。

查了下标准库,我比较好奇的是,标准库为什么使用*self & !((self.is_ascii_lowercase() as u8) << 5)来转化为大写?不过是一次判断,一次减法解决的问题,为什么要一次判断,三次位运算?

johnmave126 2021-03-05 06:56

我又查了一下标准库是一次判断,一次位运算啊

to_ascii_lowercase

ascii_change_case_unchecked

--
👇
eweca-d: 首先这里没有边界检查吧?如果你指的是检查小写字母边界的话,在rust里,使用的是b'a'这种形式就可以转化为ascii值了(u8)。另外吐槽下,根据C++反推,你的rust的upper和lower函数的ascii写反了。

解引用总要绕,这个不用绕属于语法糖范畴了吧。还可以吐槽C++解引用式使用变量需要->而rust有自动deref呢。

unsafe是特性。你既然想做危险操作,那就要unsafe。

查了下标准库,我比较好奇的是,标准库为什么使用*self & !((self.is_ascii_lowercase() as u8) << 5)来转化为大写?不过是一次判断,一次减法解决的问题,为什么要一次判断,三次位运算?

johnmave126 2021-03-04 14:10

这个标准库实现确实有点怪怪的,掏出godbolt看了一下也确实是直接判断指令数又少又简单。

我盲猜一个是为了在低优化级别下减少分支数?

--
👇
eweca-d: 首先这里没有边界检查吧?如果你指的是检查小写字母边界的话,在rust里,使用的是b'a'这种形式就可以转化为ascii值了(u8)。另外吐槽下,根据C++反推,你的rust的upper和lower函数的ascii写反了。

解引用总要绕,这个不用绕属于语法糖范畴了吧。还可以吐槽C++解引用式使用变量需要->而rust有自动deref呢。

unsafe是特性。你既然想做危险操作,那就要unsafe。

查了下标准库,我比较好奇的是,标准库为什么使用*self & !((self.is_ascii_lowercase() as u8) << 5)来转化为大写?不过是一次判断,一次减法解决的问题,为什么要一次判断,三次位运算?

eweca-d 2021-03-04 11:41

首先这里没有边界检查吧?如果你指的是检查小写字母边界的话,在rust里,使用的是b'a'这种形式就可以转化为ascii值了(u8)。另外吐槽下,根据C++反推,你的rust的upper和lower函数的ascii写反了。

解引用总要绕,这个不用绕属于语法糖范畴了吧。还可以吐槽C++解引用式使用变量需要->而rust有自动deref呢。

unsafe是特性。你既然想做危险操作,那就要unsafe。

查了下标准库,我比较好奇的是,标准库为什么使用*self & !((self.is_ascii_lowercase() as u8) << 5)来转化为大写?不过是一次判断,一次减法解决的问题,为什么要一次判断,三次位运算?

93996817 2021-03-04 09:14
用标准库提供的现成方法.
fn upper(str: &mut String) {
    str.make_ascii_uppercase();
}
fn lower(str: &mut String) {
    str.make_ascii_lowercase();
}
pub fn first_upper(str: &mut String) {
    if let Some(r) = str.get_mut(0..1) {
        r.make_ascii_uppercase();
    }
}
pub fn first_lower(str: &mut String) {
    if let Some(r) = str.get_mut(0..1){
        r.make_ascii_lowercase();
    }
}

Aya0wind 2021-03-04 01:39

你这个例子确实是特殊情况,因为是string所以才要unsafe才能去按字节改,如果参数是vec肯定是没这问题的。毕竟这两个东西在Rust里完全是天差地别,另外Rust里还有path、osstr、cstring这些在c++里都可以用string表示的东西,确实看起来搞的很复杂,只不过这些在C++里语言本身没有要求你注意而已。像windows用gbk,linux用utf8,你的代码在移植的时候由于os的编码不同导致错误,以及path一个正斜杠一个反斜杠等等,这也确实是存在的问题。C++的做法是你自己保证,Rust的做法是我强制你处理这些问题,所以显得麻烦,但也不是没有道理。

--
👇
Aya0wind: 转大小写没有问题,但是其他操作就不一定了,毕竟你按字节修改了string,就没法保证string还是正确的utf8格式了。否则就失去了区分safe代码和unsafe代码的意义了,除非把string你转vec去改,改完再用from_utf8重新验证一下正确性,这样也是safe的。如果你知道你的unsafe操作是绝对没问题的,那么就可以把unsafe封装成safe。

👇
sstudioer: // 这个中文也没问题的, utf8若是多字节首位必是b1xx0_0000 > 'z'; 我只是举个例子;

--
👇
Aya0wind: 你这例子举的不好。 Rust的String是一个utf8字符串,一个字符不是固定1字节的,你要按字节修改本来就会有破坏utf8字符串结构的可能,而C++的string就是一个bytearray,根本没规定编码,是一字节一字节访问的,本来就不一样。 Rust因为要保证不出问题才让你以字节修改字符串为unsafe操作的。你这段C++代码,要是string里面来几个中文字符,这么一操作,整个字符串就要乱了,后面再按utf8规则去使用,搞不好就要出错甚至直接导致程序崩掉,只是无视了问题,而不是解决了问题罢了。

Aya0wind 2021-03-04 01:22

转大小写没有问题,但是其他操作就不一定了,毕竟你按字节修改了string,就没法保证string还是正确的utf8格式了。否则就失去了区分safe代码和unsafe代码的意义了,除非把string你转vec去改,改完再用from_utf8重新验证一下正确性,这样也是safe的。如果你知道你的unsafe操作是绝对没问题的,那么就可以把unsafe封装成safe。

👇
sstudioer: // 这个中文也没问题的, utf8若是多字节首位必是b1xx0_0000 > 'z'; 我只是举个例子;

--
👇
Aya0wind: 你这例子举的不好。 Rust的String是一个utf8字符串,一个字符不是固定1字节的,你要按字节修改本来就会有破坏utf8字符串结构的可能,而C++的string就是一个bytearray,根本没规定编码,是一字节一字节访问的,本来就不一样。 Rust因为要保证不出问题才让你以字节修改字符串为unsafe操作的。你这段C++代码,要是string里面来几个中文字符,这么一操作,整个字符串就要乱了,后面再按utf8规则去使用,搞不好就要出错甚至直接导致程序崩掉,只是无视了问题,而不是解决了问题罢了。

作者 sstudioer 2021-03-04 00:45

// 这个中文也没问题的, utf8若是多字节首位必是b1xx0_0000 > 'z'; 我只是举个例子;

--
👇
Aya0wind: 你这例子举的不好。 Rust的String是一个utf8字符串,一个字符不是固定1字节的,你要按字节修改本来就会有破坏utf8字符串结构的可能,而C++的string就是一个bytearray,根本没规定编码,是一字节一字节访问的,本来就不一样。 Rust因为要保证不出问题才让你以字节修改字符串为unsafe操作的。你这段C++代码,要是string里面来几个中文字符,这么一操作,整个字符串就要乱了,后面再按utf8规则去使用,搞不好就要出错甚至直接导致程序崩掉,只是无视了问题,而不是解决了问题罢了。

Aya0wind 2021-03-04 00:39

你这例子举的不好。 Rust的String是一个utf8字符串,一个字符不是固定1字节的,你要按字节修改本来就会有破坏utf8字符串结构的可能,而C++的string就是一个bytearray,根本没规定编码,是一字节一字节访问的,本来就不一样。 Rust因为要保证不出问题才让你以字节修改字符串为unsafe操作的。你这段C++代码,要是string里面来几个中文字符,这么一操作,整个字符串就要乱了,后面再按utf8规则去使用,搞不好就要出错甚至直接导致程序崩掉,只是无视了问题,而不是解决了问题罢了。

1 共 10 条评论, 1 页