在Rust中将{integer}转换为f32

Mad*_*key 2 rust

我想将值转换{integer}f32:

struct Vector3 {
    pub x: f32,
    pub y: f32,
    pub z: f32,
}

for x in -5..5 {
    for y in -5..5 {
        for z in -5..5 {
            let foo: Vector3 = Vector3 { x: x, y: y, z: z };
            // do stuff with foo
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

编译器因类型不匹配错误而窒息(期待f32但是获得{integer}).不幸的是我不能简单地改变Vector3.我正在用这个提供一个C-API.

有没有简单和简洁的方式,我可以转换x,y并且z{integer}f32

我想没有内置的转换,从i32{integer}f32,因为它可能是在某些情况下有损耗的.但是,在我的情况下,我使用的范围非常小,这不会成为问题.所以我想告诉编译器反正转换该值.

有趣的是,以下工作:

for x in -5..5 {
    let tmp: i32 = x;
    let foo: f32 = tmp as f32;
}
Run Code Online (Sandbox Code Playgroud)

我使用的只是一个foo和一个x,所以这很快变得可怕.

此外,这有效:

for x in -5i32..5i32 {
    let foo: f32 = x as f32;
    // do stuff with foo here
}
Run Code Online (Sandbox Code Playgroud)

但随着我的用例,这变成了:

for x in -5i32..5i32 {
    for y in -5i32..5i32 {
        for z in -5i32..5i32 {
            let foo: Vector3 = Vector3 {
                x: x as f32,
                y: y as f32,
                z: z as f32,
            };
            // do stuff with foo
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我认为这是非常难以理解的,并且对于简单的转换来说是不合理的.

我在这里错过了什么?

tre*_*tcl 7

我不确定你为什么觉得需要i32在使用时指定s as,因为这很好(游乐场):

for x in -5..5 {
    for y in -5..5 {
        for z in -5..5 {
            let foo = Vector3 { // no need to specify the type of foo
                x: x as f32,
                y: y as f32,
                z: z as f32,
            };
            // etc.
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

正如Klitos Kyriacou的回答所指出的那样,没有这样的类型{integer}; 编译器提供该错误消息,因为它无法推断出具体类型x.它实际上并不重要,因为在Rust 中没有从整数类型到浮点类型的隐式转换,或者从整数类型到其他整数类型的隐式转换.实际上,Rust在任何类型的隐式转换上都很短(最明显的例外是Deref强制转换).

铸造用型as允许编译调和的类型不匹配,并且它最终将填补{integer}i32(不受约束的整数文字始终默认为i32,而不是在这种情况下,具体类型事项).

您可能更喜欢的另一个选项,特别是如果您使用x,y以及z在循环中用于其他目的,是用f32版本遮蔽它们而不是创建新名称:

for x in -5..5 {
    let x = x as f32;
    for y in -5..5 {
        let y = y as f32;
        for z in -5..5 {
            let z = z as f32;
            let foo = Vector3 { x, y, z };
            // etc.
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

(您不必编写x: x, y: y, z: z- 当变量名与结构成员名称相同时,Rust会做正确的事.)

另一个选项(最后一个,我保证)是使用map以下方法转换迭代器:

for x in (-5..5).map(|x| x as f32) {
    for y in (-5..5).map(|y| y as f32) {
        for z in (-5..5).map(|z| z as f32) {
            let foo = Vector3 { x, y, z };
            // etc.
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然而,它比以前的版本更密集,可能更难阅读.


She*_*ter 5

既然其他人都在回答,我将提出一个迭代器风格的解决方案。这使用Itertools::cartesian_product而不是for循环:

extern crate itertools;

use itertools::Itertools;

fn main() {
    fn conv(x: i32) -> f32 { x as f32 }

    let xx = (-5..5).map(conv);
    let yy = xx.clone();
    let zz = xx.clone();

    let coords = xx.cartesian_product(yy.clone().cartesian_product(zz));
    let vectors = coords.map(|(x, (y, z))| Vector3 { x, y, z });
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,闭包尚未实现Clone因此我使用了一个小函数来执行映射。这些确实落实了Clone

如果您想要一个辅助方法:

extern crate itertools;

use itertools::Itertools;
use std::ops::Range;

fn f32_range(r: Range<i32>) -> std::iter::Map<Range<i32>, fn(i32) -> f32> {
    fn c(x: i32) -> f32 { x as _ }
    r.map(c)
}

fn main() {
    let xx = f32_range(-5..5);
    let yy = f32_range(-5..5);
    let zz = f32_range(-5..5);

    let coords = xx.cartesian_product(yy.cartesian_product(zz));
    let vectors = coords.map(|(x, (y, z))| Vector3 { x, y, z });
}
Run Code Online (Sandbox Code Playgroud)