简体   繁体   English

在Rust中将{integer}转换为f32

[英]Converting {integer} to f32 in Rust

I want to convert a value from {integer} to f32 : 我想将值从{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
        }
    }
}

The compiler chokes on this with a type mismatch error (expecting f32 but getting {integer} ). 编译器因类型不匹配错误而阻塞了此错误(期望f32但得到{integer} )。 Unfortunately I can not simply change Vector3 . 不幸的是,我不能简单地更改Vector3 I'm feeding a C-API with this. 我为此提供了C-API。

Is there any easy and concise way I can convert x , y and z from {integer} to f32 ? 有什么简单明了的方法可以将xyz{integer}f32

I guess there is no builtin conversion from i32 or {integer} to f32 because it could be lossy in certain situations. 我猜没有从i32{integer}f32内置转换,因为它在某些情况下可能有损。 However, in my case the range I'm using is so small that this wouldn't be an issue. 但是,就我而言,我所使用的范围很小,因此这不会成为问题。 So I would like to tell the compiler to convert the value anyways. 所以我想告诉编译器无论如何都要转换值。

Interestingly, the following works: 有趣的是,以下作品:

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

I'm using a lot more that just one foo and one x so this turns hideous really fast. 我使用的不仅仅是一个foo和一个x,所以这变得非常可怕。

Also, this works: 同样,这可行:

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

But with my usecase this turns into: 但是用我的用例,它变成了:

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

Which I think is pretty unreadable and an unreasonable amount of cruft for a simple conversion. 对于简单的转换,我认为这是非常难以理解的,并且不合理。

What am I missing here? 我在这里想念什么?

I'm not sure why you felt the need to specify the i32 s when using as , since this works fine ( playground ): 我不确定您为什么在使用as时会觉得需要指定i32 ,因为这样可以很好地工作( 游乐场 ):

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

As Klitos Kyriacou's answer observes, there is no such type as {integer} ; 正如克里托斯·基里亚库(Klitos Kyriacou)的回答所观察到的,没有{integer}这样的类型; the compiler gives that error message because it couldn't infer a concrete type for x . 编译器给出该错误消息,因为它无法推断x的具体类型。 It doesn't actually matter, because there are no implicit conversions from integer types to floating-point types in Rust, or from integer types to other integer types, for that matter. 实际上并不重要,因为在此情况下,Rust中没有从整数类型到浮点类型的隐式转换,也没有从整数类型到其他整数类型的隐式转换。 In fact, Rust is quite short on implicit conversions of any sort (the most notable exception being Deref coercions). 实际上,Rust在任何形式的隐式转换上都非常短(最著名的例外是Deref强制)。

Casting the type with as permits the compiler to reconcile the type mismatch, and it will eventually fill in {integer} with i32 ( unconstrained integer literals always default to i32 , not that the concrete type matters in this case). 使用as i32转换类型使编译器可以协调类型不匹配的情况,最终它将用i32填充{integer}无约束的整数常量始终默认为i32 ,在这种情况下,具体类型并不重要)。

Another option you may prefer, especially if you use x , y and z for other purposes in the loop, is to shadow them with f32 versions instead of creating new names: 您可能更喜欢另一种选择,尤其是在循环中将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.
        }
    }
}

(You don't have to write x: x, y: y, z: z -- Rust does the right thing when the variable name is the same as the struct member name.) (您不必写x: x, y: y, z: z -当变量名与结构成员名相同时,Rust做对了。)

Another option (last one, I promise) is to convert the iterators instead using map : 另一个选择(我保证,最后一个选择)是转换迭代器,而不是使用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.
        }
    }
}

However it is a little more dense and may be harder to read than the previous version. 但是,它比以前的版本更密集,可能更难阅读。

The only integer types available are i8 , i16 , i32 , etc. and their unsigned equivalents. 可用的唯一整数类型是i8i16i32等,以及它们的无符号等效项。 There is no such type as {integer} . 没有{integer}这样的类型。 This is just a placeholder emitted by the compiler before it has determined the actual type by inference from the whole-method context. 这只是编译器在通过从整个方法上下文进行推断来确定实际类型之前发出的占位符。

The problem is that, at the point where you call Vector3 {x: x as f32, y: y as f32, z: z as f32} , it doesn't yet know exactly what x, y and z are, and therefore doesn't know what operations are available. 问题是,在调用Vector3 {x: x as f32, y: y as f32, z: z as f32}Vector3 {x: x as f32, y: y as f32, z: z as f32} ,它尚不确切知道x,y和z是什么,因此不知道不知道可以进行哪些操作。 It could use the operations given to determine the type, if it was a bit more intelligent; 如果它更智能,它可以使用给定的操作来确定类型。 see bug report for details. 有关详细信息,请参见错误报告

There is a conversion from i32 to f32 , so you should be able to do this: i32f32都有转换,因此您应该可以执行以下操作:

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

Since everyone else is answering, I'll chime in with an iterator-flavored solution. 由于其他所有人都在回答,因此我将介绍迭代器风格的解决方案。 This uses Itertools::cartesian_product instead of the for loops: 这使用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 });
}

Unfortunately, closures don't yet implement Clone , so I used a small function to perform the mapping. 不幸的是,闭包尚未实现Clone ,因此我使用了一个小函数来执行映射。 These do implement Clone . 这些确实实现了Clone

If you wanted a helper method: 如果需要帮助方法:

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

From<i16> is implemented for f32 . From<i16>f32 实现

So it should be possible to 所以应该有可能

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

Of course if your iteration uses values bigger than i16 ( i32 or i64 ) this is no longer possible in a safe way and you have to try another way. 当然,如果您的迭代使用的值大于i16i32i64 ),则这将不再是安全的方式,您必须尝试另一种方式。

As many problems in Computer Science, it can be solved by applying another layer of indirection. 与计算机科学中的许多问题一样,可以通过应用另一层间接解决。

For example, defining a constructor for Vec3 : 例如,为Vec3定义一个构造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);
            }
        }
    }
}

You can use a plethora of other methods (generics, builders, etc...); 您可以使用多种其他方法(泛型,构建器等); but a good old constructor is just the simplest. 但是一个好的旧构造函数只是最简单的。

Another solution this time using a function and traits. 这次使用功能和特征的另一个解决方案。 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
            }
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM