如何在rust中正确设置通用收集类型

问题描述 投票:2回答:2

我正在尝试与Free Pascal 3.0.4相比,在FreeBSD 12下测试Rust 1.39中的某些语言功能,以比较简单的通用点集,其中包含由字符串键寻址的2D点。不幸的是,泛型类型声明的代码无法在很早的状态下编译,并以以下代码终止:

error[E0106]: missing lifetime specifier
  --> src/main.rs:11:31
   |
11 |     type TPointMap = BTreeMap<&TString, TPoint>;
   |     

我必须如何重写Rust代码?

详细信息:

为了测试语言行为,我在Rust和Pascal中编写了两个小程序,“语法上”针对相同的上下文。 Pascal程序是一个简单的声明:

  1. 某些普通类型,记录类型和通用地图容器,
  2. 将用于定义点,将点存储在新分配的地图中,]
  3. 通过键搜索点并将数据写入STDIO
  4. 并最终释放地图。
program test;
uses fgl; { Use the free pascal generics library }
type
   TDouble   = Double; { Define a 64 bit float } 
   TString   = String; { Define a string type  } 
   TPoint    = record  { Define a point record }
                  X : TDouble; { Coordinate X }
                  Y : TDouble; { Coordinate Y }
               end; 
   { Define a map of points with strings as key }
   TPointMap = specialize TFPGMap<TString, TPoint>;

{ Test program } 
var
   map   : TPointMap; { Declare the map variable }
   point : TPoint;    { Declare a point variable }
   found : TPoint;    { Varaiable for a found point }
   key   : TString;   { Variable to address the point in the map } 
begin
   map := TPointMap.create; { Allocate a new ma container }
   with point do begin      { Set the point variable }
      x := 1.0; y := 2.0;
   end;
   key := '123';              { Set the key address to '123'  }
   map.add(key,point);        { Store the point in the map }

   { Search the point an write the result in the rusty way }
   case map.TryGetData(key, found)  of
     true  : writeln('X: ',found.X:2;, ' Y:', found.Y:2:2);
     false : writeln('Key ''',key,''' not found');
   end;
   map.free;  { De-allocate the map }
   { Plain types are de-allocated by scope }
end.   

程序编译并给我:

$ ./main 
X: 1.00 Y:2.00

这是我不正确的Rust版本代码:

use std::collections::BTreeMap; // Use a map from the collection 

type TDouble = f64; // Define the 64 bit float type
type TString = str; // Define the string type
struct TPoint {     // Define the string type
    x: TDouble,     // Coordinate X
    y: TDouble,     // Coordinate Y
}

// Define a map of points with strings as key 
type TPointMap = BTreeMap<&TString, TPoint>;

// Test program
fn main() {
    let point = TPoint { x: 1.0, y: 2.0 }; // Declare and define the point variable
    let mut map = TPointMap::new();        // Declare the map and allocate it
    let key: TString = "123";              // Declare and define the address of point 
    map.insert(&key, point);               // Add the point to the map 
    // search the point and print it
    match map.get(&key) {
        Some(found) => println!("X: {} Y: {}", found.X, found.y),
        None => println!("Key '{}' not found", key),
    }
   // map is de-allocated by scope
}

注:由于借用和所有权概念,我知道某些代码行不能在Rust代码中使用。线

 match map.get(&key)...

是其中之一。

generics rust
2个回答
5
投票

freepascal版本大致相同,TString可能应该是String而不是strfreepascal字符串是(depending on some flags)指针,长度和字符堆分配的数组。 (几乎)就是Stringstr只是字符数组,没有大小限制,因此它总是必须位于某种(胖)指针之后。

一旦进行了更改,只有几件事可以修复代码。 TPointMap需要使用期限参数,因为它使用了引用类型。参考的生命周期必须来自某个地方,因此我们使TPointMap在该生命周期中是通用的。

type TPointMap<'a> = BTreeMap<&'a TString, TPoint>;

如果您的用例允许,您也可以考虑简单地使用BTreeMap<TString, TPoint>

我们需要做一些转换才能声明key: TString。字符串文字的类型为'static str,但是有一种简单的to_string方法将其转换为String

let key: TString = "123".to_string(); 

最后,在found.X中有一个错字。

Some(found) => println!("X: {} Y: {}", found.x, found.y),

总共,我们有

use std::collections::BTreeMap; // Use a map from the collection

type TDouble = f64; // Define the 64 bit float type
type TString = String; // Define the string type
struct TPoint {
    // Define the string type
    x: TDouble, // Coordinate X
    y: TDouble, // Coordinate Y
}

// Define a map of points with strings as key
type TPointMap<'a> = BTreeMap<&'a TString, TPoint>;

// Test program
fn main() {
    let point = TPoint { x: 1.0, y: 2.0 }; // Declare and define the point variable
    let mut map = TPointMap::new(); // Declare the map and allocate it
    let key: TString = "123".to_string(); // Declare and define the address of point
    map.insert(&key, point); // Add the point to the map
                             // search the point and print it
    match map.get(&key) {
        Some(found) => println!("X: {} Y: {}", found.x, found.y),
        None => println!("Key '{}' not found", key),
    }
    // map is de-allocated by scope
}

(playground)

另请参阅What are the differences between Rust's String and str?


4
投票

您看到此失败的原因是因为您需要一生的时间作为参考。 Rust目前无法知道您的参考应持续多长时间。

一旦解决此问题,您将遇到无法创建类型为String的变量的事实,因为它没有大小,并且编译器无法在编译时告知要分配多少空间。

您可以在此处进行的最简单,最小的更改是更改以下几行:

str

str

((由于Rust区分大小写,因此您还需要在type TString = &'static str; 中将type TPointMap = BTreeMap<TString, TPoint>; 小写。)

这将告诉编译器您的字符串类型为x,这是字符串文字的类型,因为它们的寿命与程序一样长。

您还可以通过执行以下操作,使其与任意寿命的found.X引用一起使用:

&'static str

str

但是,在许多程序中,您可能希望插入不限于字符串文字的字符串,并避免在映射的整个生命周期内借用必要的借项,例如,如果要从函数返回映射。在这种情况下,使用拥有的type TString<'a> = &'a str; 对象可能很有意义,这样该对象才能与地图一样长。在这种情况下,您的代码将如下所示:

type TPointMap<'a> = BTreeMap<TString<'a>, TPoint>;
© www.soinside.com 2019 - 2024. All rights reserved.