如何在不分配堆的情况下转换切片引用的元素?

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

假设有一个参数数组需要在 SQL 查询中使用。每个参数必须是一个

&dyn ToSql
,这已经为
&str
实现了。

需要将对象同时用作

&dyn ToSql
&str
,如下面的示例所示,需要实现
Display
才能打印出来。

let params = ["a", "b"];

// this works but allocates
// let tx_params = &params
//             .iter()
//             .map(|p| p as &(dyn ToSql + Sync))
//             .collect::<Vec<_>>();

// this is ideal, doesn't allocate on the heap, but doesn't work
params.map(|p| p as &(dyn ToSql + Sync));

// this has to compile, so can't just crate `params` as [&(dyn ToSql + Sync)] initially
println!("Could not insert {}", params);

错误:

   Compiling playground v0.0.1 (/playground)
error[E0277]: the trait bound `str: ToSql` is not satisfied
  --> src/main.rs:14:20
   |
14 |     params.map(|p| p as &(dyn ToSql + Sync));
   |                    ^ the trait `ToSql` is not implemented for `str`
   |
   = help: the following implementations were found:
             <&'a str as ToSql>
   = note: required for the cast to the object type `dyn ToSql + Sync`

error[E0277]: the size for values of type `str` cannot be known at compilation time
  --> src/main.rs:14:20
   |
14 |     params.map(|p| p as &(dyn ToSql + Sync));
   |                    ^ doesn't have a size known at compile-time
   |
   = help: the trait `Sized` is not implemented for `str`
   = note: required for the cast to the object type `dyn ToSql + Sync`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` due to 2 previous errors

特征

ToSql
没有为
str
实现,但它是为
&str
实现的,但是我们借用checked不会让我们借用
p
,即使我们没有对数据做任何事情,除了将其转换为新类型之外。

游乐场

rust memory-management dynamic-memory-allocation allocation
2个回答
1
投票

我同意@Caesar 对此的看法,但是实际上你可以在没有堆分配的情况下做到这一点。

您可以使用

<[T; N]>::each_ref()
(此方法将
&[T; N]
转换为
[&T; N]
):

params.each_ref().map(|p| p as &(dyn ToSql + Sync));

游乐场


1
投票

我一个月前为此奋斗过,我的一般建议是:不要打扰。实际查询比分配重得多。

情况有点混乱,因为你需要一个

&ToSql
,但是
ToSql
是为&str
实现的
,所以你需要两个数组:一个
[&str]
和一个
[&ToSql]
,其元素引用
 &str
s - 所以
[&ToSql]
的内容是双重引用。我认为没有一种简单的方法可以在不分配的情况下实现这一目标。 (
let params: [&&str; 2] = params.iter().collect::<Vec<_>>().try_into().unwrap();
有效,分配可能会被优化。存在夜间或不安全的方式,请参阅@ChayimFriedman的答案。)

在这种情况下,您可以通过最初声明来解决:

let params = [&"a", &"b"];

通过使用迭代器,而不是数组:

let iter = params.iter().map(|p| p as &(dyn ToSql + Sync));
client.query_raw("select * from foo where id in ?", iter);

就我而言,我无法执行类似的操作,因为我使用的是执行,而不是查询,并且

execute_raw
仅存在于
tokio-postgres
上,而不存在于
postgres
上。所以要小心这些陷阱。

© www.soinside.com 2019 - 2024. All rights reserved.