質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

Q&A

解決済

3回答

1079閲覧

Rustで巨大な配列データやネットワークストリームをファイルに書き込みながらシリアライズする方法

v_v

総合スコア47

Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

0グッド

0クリップ

投稿2022/05/25 12:26

編集2022/05/25 12:29

やりたいこと

ネットワークからメッセージを受信した瞬間に受信したデータをメモリ内にバッファリングせずJSONでシリアライズし、即時吐き出したい場合はどうしたら良いでしょう。

受信したメッセージは配列の一要素として吐き出したいです。
(ログを一つのJSONオブジェクトとして吐き出すようなイメージです。)

雰囲気(ビルドエラーになります)

rust

1struct SeqSerializer{ 2 seq: Compound<'a, &'a mut dyn Write, CompactFormatter>, 3} 4impl SeqSerializer { 5 // 初期化 6 fn new() -> Self { 7 let writer = std::io::stdout(); 8 let mut seq = serde_json::Serializer::new(writer); 9 let mut seq = seq.serialize_seq(None).unwrap(); 10 SeqSerializer { seq: seq } // ↑がキャプチャしてるので返せない。 11 } 12 // 通信がオープンした 13 fn open(&self) {} 14 // メッセージを受信した 15 fn receive_message(&self, msg: String) { 16 seq.serialize_element(msg); 17 } // 通信がクローズされた 18 fn close(&self) { 19 seq.end(); 20 } 21} 22

依存関係は以下のとおりです。

toml

1[dependencies] 2serde_derive = "1.0.115" 3serde = "1.0.115" 4serde_json = "1.0.57"

コメントで記載した通り、seq._serialize_seqがキャプチャしてしまうため、意図したコードを書くことができません。
こういった
「初期化」
「使用」
「後処理」
をバラバラに使う場合、どのようにすれば書くことができるのでしょうか。

それともこのような使い方は、そもそもおかしいからやめろということでしょうか。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

前の回答のように SeqSerializerを使用する関数を渡す形にできず、どうしても初期化でSeqSerializerを返す形にしたい場合は、処理を全て非同期ランタイム上で行うという手もあります。

ただし、この方法は処理が複雑になってくると対応しきれなくなるかもしれません。(例えば、前の回答は同様の手法を二重に組み合わせることも容易ですが、こちらはかなり難しくなります。)

toml

1[dependencies] 2serde_derive = "1.0.115" 3serde = "1.0.115" 4serde_json = "1.0.57" 5tokio = { version = "1.18.2", features = [ 6 "sync", 7 "rt", 8 "macros", 9 "rt-multi-thread", 10] }

rust

1use serde::{ser::SerializeSeq, Serializer}; 2use serde_json::Result; 3use tokio::{ 4 spawn, 5 sync::{mpsc, oneshot}, 6 task::JoinHandle, 7}; 8 9struct SeqSerializer { 10 sender: mpsc::UnboundedSender<(String, oneshot::Sender<Result<()>>)>, 11 handle: JoinHandle<Result<()>>, 12} 13impl SeqSerializer { 14 fn open() -> Self { 15 let (sender, mut reciever) = 16 mpsc::unbounded_channel::<(String, oneshot::Sender<Result<()>>)>(); 17 let handle = spawn(async move { 18 let writer = std::io::stdout(); 19 let mut seq = serde_json::Serializer::new(writer); 20 let mut seq = seq.serialize_seq(None).unwrap(); 21 while let Some((msg, sender)) = reciever.recv().await { 22 sender.send(seq.serialize_element(&msg)).unwrap(); 23 } 24 seq.end() 25 }); 26 Self { sender, handle } 27 } 28 async fn receive_message(&self, msg: String) -> Result<()> { 29 let (sender, receiver) = oneshot::channel(); 30 self.sender.send((msg, sender)).unwrap(); 31 receiver.await.unwrap() 32 } 33 async fn close(self) -> Result<()> { 34 drop(self.sender); 35 self.handle.await.unwrap() 36 } 37} 38 39#[tokio::main] 40async fn main() -> Result<()> { 41 let s = SeqSerializer::open(); 42 s.receive_message("abc".into()).await?; 43 s.receive_message("def".into()).await?; 44 s.close().await?; 45 Ok(()) 46}

投稿2022/05/26 03:52

編集2022/05/26 03:59
frozenlib

総合スコア51

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

初期化の処理を

SeqSerializerを返す」

から

SeqSerializerを使用する関数を渡す」

形に変更すると比較的少ない変更で対応できます。

rust

1use serde::{ser::SerializeSeq, Serializer}; 2use serde_json::ser::{CompactFormatter, Compound}; 3use std::io::Write; 4 5struct SeqSerializer<'a> { 6 seq: Compound<'a, &'a mut dyn Write, CompactFormatter>, 7} 8impl<'a> SeqSerializer<'a> { 9 // 初期化 10 fn new_with(f: impl FnOnce(SeqSerializer)) { 11 let mut writer = std::io::stdout(); 12 let writer: &mut dyn Write = &mut writer; 13 let mut seq = serde_json::Serializer::new(writer); 14 let seq = seq.serialize_seq(None).unwrap(); 15 f(SeqSerializer { seq }) 16 } 17 // 通信がオープンした 18 fn open(&self) {} 19 // メッセージを受信した 20 fn receive_message(&mut self, msg: &str) { 21 self.seq.serialize_element(msg).unwrap(); 22 } // 通信がクローズされた 23 fn close(self) { 24 self.seq.end().unwrap(); 25 } 26} 27 28fn main() { 29 SeqSerializer::new_with(|mut s| { 30 s.open(); 31 s.receive_message("abc"); 32 s.receive_message("def"); 33 s.close(); 34 }); 35}

投稿2022/05/26 03:50

frozenlib

総合スコア51

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

愚直ですが、出したいデータのトップレベルが配列と確定しているなら、1エントリごとにJSONにエンコードして吐き出すという方法があります。

rust

1use std::fs::File; 2use std::io::{BufWriter, Write}; 3 4use anyhow::Result; 5use serde::Serialize; 6use serde_json::to_writer; 7 8fn main() -> Result<()> { 9 let f = File::create("sample.json")?; 10 let mut f = BufWriter::new(f); 11 12 writeln!(f, "[")?; 13 14 for i in 0..100 { 15 if i > 0 { 16 writeln!(f, ",")?; // 前のエントリの後端のカンマ 17 } 18 19 let value = receive_data(); 20 to_writer(&mut f, &value)?; 21 } 22 23 write!(f, "\n]")?; 24 25 Ok(()) 26} 27 28#[derive(Serialize)] 29pub struct SampleStruct { 30 name: String, 31 age: u64, 32} 33 34fn receive_data() -> SampleStruct { 35 SampleStruct { 36 name: "Douglas Adams".to_string(), 37 age: 42, 38 } 39}

投稿2022/05/26 01:49

編集2022/05/26 01:53
IgaguriMK

総合スコア148

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問