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

发布于 2025-01-15 11:18:22 字数 2044 浏览 5 评论 0 原文

假设 SQL 查询中需要使用一组参数。每个参数必须是 &dyn ToSql,它已针对 &str 实现。

需要将该对象同时用作 &dyn ToSql&str,如下面的示例所示,其中需要实现 Display< /code> 以便打印出来。

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 ,尽管我们除了将其转换为新类型之外没有对数据执行任何操作。

游乐场

Let's suppose there's an array of parameters that need to be used in SQL query. Each parameter must be a &dyn ToSql,which is implemented already for &str.

The need arises to use the object both as &dyn ToSql and as &str, like in the example down below, where it needs to implement Display in order to be printed out.

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

// this works but allocates
// let tx_params = ¶ms
//             .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);

Error:

   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

The trait ToSql isn't implemented for str, but it is for &str, however we borrow checked won't let us borrow p here, even though we're not doing anything with the data, except cast it as a new type.

Playground

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

乙白 2025-01-22 11:18:22

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

您可以使用 <[T; N]>::each_ref() (此方法将 &[T; N] 转换为 [&T; N]):

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

游乐场

I agree with @Caesar's take on this, however you actually can do that without heap allocations.

You can use <[T; N]>::each_ref() for that (this method converts &[T; N] to [&T; N]):

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

Playground.

长伴 2025-01-22 11:18:22

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

这种情况有点令人困惑,因为您需要一个 &ToSql,但是 ToSql 已实现 &str,因此您需要两个数组:一个 [&str] 和一个 [&ToSql],其元素引用 < code>&strs - 因此 [&ToSql] 的内容是双重引用。我认为没有一种简单的方法可以在不分配的情况下实现这一目标。 (let params: [&&str; 2] = params.iter().collect::>().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 上。所以要小心这些类型的陷阱。

I fought this a month ago and my general recommendation is: Don't bother. The actual query is so much heavier than an allocation.

The situation is a bit confusing, because you need an &ToSql, but ToSql is implemented for &str, so you need two arrays: One [&str], and one [&ToSql], whose elements reference &strs - so the contenst of [&ToSql] are double references. I don't see an easy way of achieving that without allocating. (let params: [&&str; 2] = params.iter().collect::<Vec<_>>().try_into().unwrap(); works and the allocation will likely be optimized out. Nighly or unsafe ways exist, see @ChayimFriedman's answer.)

In this case, you can work around either by initially declaring:

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

by using an iterator, not an array:

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

In my case, I wasn't able to do anything like this because I was using execute, not query, and execute_raw exists only on tokio-postgres, but not on postgres. So beware of these kinds of pitfalls.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文