在Rust中将{integer}转换为f32

时间:2017-11-16 12:56:29

标签: 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
        }
    }
}

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

我是否可以轻松简洁地将xyz{integer}转换为f32

我想从i32{integer}f32没有内置转化,因为在某些情况下它可能会有损。但是,在我的情况下,我使用的范围非常小,这不会成为问题。所以我想告诉编译器无论如何都要转换它。

有趣的是,以下工作:

for x in -5..5 {
    let tmp: i32 = x;
    let foo: f32 = tmp as f32;
}

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

此外,这也有效:

for x in -5i32..5i32 {
    let foo: f32 = x as f32;
    // do stuff with foo here
}

但是用我的用例变成了:

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
        }
    }
}

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

我在这里缺少什么?

6 个答案:

答案 0 :(得分:7)

我不确定您在使用i32时是否需要指定as,因为这样可以正常使用playground):

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.
        }
    }
}

正如Klitos Kyriacou's answer所述,没有{integer}这样的类型;编译器提供该错误消息,因为它无法推断x的具体类型。它实际上并不重要,因为就此而言,Rust中的整数类型与浮点类型之间存在隐式转换,或者从整数类型转换为其他整数类型。实际上,Rust在任何类型的隐式转换方面都很短(最值得注意的例外是Deref强制转换)。

使用as转换类型允许编译器协调类型不匹配,并最终使用{integer} unconstrained integer literals always default to i32填充i32,而不是具体类型在这种情况下很重要。)

您可能更喜欢的另一个选项,特别是如果您在循环中使用xyz用于其他目的,则会使用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.
        }
    }
}

(你不必写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.
        }
    }
}

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

答案 1 :(得分:3)

唯一可用的整数类型是i8i16i32等,以及它们的无符号等价物。没有{integer}这样的类型。这只是编译器在通过整个方法上下文推断确定实际类型之前发出的占位符。

问题在于,在您调用Vector3 {x: x as f32, y: y as f32, z: z as f32}时,它还不确切知道x,y和z是什么,因此不知道哪些操作可用。它可以使用给定的操作来确定类型,如果它更聪明一点;有关详细信息,请参阅bug report

i32转换为f32,因此您应该可以执行此操作:

let foo = Vector3 {x: (x as i32) as f32, y: (y as i32) as f32, z: (z as i32) as f32};

答案 2 :(得分:3)

由于其他人都在回答,我将使用迭代器风格的解决方案。这使用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 });
}

不幸的是,闭包不会 实现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 });
}

答案 3 :(得分:2)

对于From<i16>

f32 is implemented

所以应该可以

for x in -5..5 {
    for y in -5..5 {
        for z in -5..5 {
            let foo: Vector3 = Vector3 {
                 x: f32::from(x),
                 y: f32::from(y),
                 z: f32::from(z),
            };
            // do stuff with foo
        }
    }
}

当然,如果您的迭代使用的值大于i16i32i64),则无法以安全的方式进行此操作,您必须尝试其他方式。

答案 4 :(得分:2)

在计算机科学中存在许多问题,可以通过应用另一层间接来解决。

例如,为Vec3定义构造函数:

impl Vec3 {
    fn new(x: i16, y: i16, z: i16) -> Vec3 {
        Vec3 { x: x as f32, y: y as f32, z: z as f32 }
    }
}

fn main() {
    for x in -5..5 {
        for y in -5..5 {
            for z in -5..5 {
                let foo = Vector3::new(x, y, z);
                println!("{:?}", foo);
            }
        }
    }
}

你可以使用过多的其他方法(泛型,建造者等......);但是一个好的旧构造函数才是最简单的。

答案 5 :(得分:1)

这次使用函数和特征的另一种解决方案。 playground

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

impl Vector3 {
    pub fn new<T: Into<f32>>(a: T, b: T, c: T) -> Vector3 {
        Vector3 {
            x: a.into(),
            y: b.into(),
            z: c.into(),
        }
    }
}

fn main() {
    for x in -5..5i8 {
        for y in -5..5i8 {
            for z in -5..5i8 {
                let foo: Vector3 = Vector3::new(x, y, z);
                // do stuff with foo
            }
        }
    }
}