Я хочу создать структуру данных, которая будет хранить и изменять значения по мере поступления данных. Хотя я научился выполнять асинхронные вызовы REST API, использовать serde_json и распечатывать значения, у меня возникла проблема с хранением данных.
Вот структура старой, используемой в настоящее время версии массива в PHP:
$data[$manufacturer][$model_id]['current_bid']=3400.50;
$data[$manufacturer][$model_id]['last_bid_time']=1649266798;
Итак, скажем, это:
$data["volvo"]["V60"]['current_bid']=3400.50;
$data["volvo"]["V60"]['last_bid_time']=1649266798;
$data["volvo"]["V90"]['current_bid']=5200.50;
$data["volvo"]["V90"]['last_bid_time']=1649266799;
Идентификаторы модели представляют собой целые числа со знаком (u8
подходит, в приведенном выше примере использовалась строка для пояснения), текущие ставки — это f64
, а last_bid_time
— это эпоха UNIX. Насколько я понимаю, ящик chrono:datetime
отправляет обратно i64
через utc::now()
.
Ставки на текущие автомобили могут меняться по мере поступления данных, и могут появиться новые модели, поэтому я хотел бы добавить их в часть volvo
этой структуры данных. Для каждой конкретной модели ставка будет обновляться только с новой ценой и last_bid_time
.
Как создать такую переменную? В PHP это просто, в Rust это должно быть сверхпроизводительно и легковесно, но я не знаю, как это сделать.
Насколько я понимаю, векторы для этого не годятся, так как они «ограничены во время компиляции»? а хэшмапы нет?
Я пытался использовать хэш-карты, но постоянно получаю сообщение об ошибке, что я должен поместить current_bid
и last_bid_time
как кортеж, из-за чего hashmap.insert
не работает.
Должен ли я использовать структуры?
struct VehicleBid {
current_bid: f64,
last_bid_time: i64,
}
struct VehicleModel {
model_id: i8,
}
Struct Manufacturer {
name: String,
}
Struct Bids{
Manufacturer {
VehicleModel {
VehicleBid
}
}
}
Я дошел до этого, но не знаю, как хранить данные в этой переменной:
let mut data: HashMap<String, HashMap<u8, HashMap<f64, i64>>> = HashMap::new();
Похоже, на ваш вопрос могут ответить ответы Как эффективно искать и вставлять в HashMap?. Если нет, пожалуйста, редактировать свой вопрос, чтобы объяснить различия. В противном случае мы можем пометить этот вопрос как уже отвеченный.
Во-первых: этот вариант использования звучит так, будто вам нужна база данных. Это позволит управлять такими вещами, как одновременные соединения, многоэтапные транзакции, сохранение и восстановление. При этом простая функциональность чтения/замены, которую вы описали, абсолютно возможна с использованием Rust HashMap
s.
Основная проблема с тем, что вы показываете, - это косвенность. Лучше использовать одну карту, где ключ — (manufacture, id)
, а не две карты, одна из которых сопоставляет производителей со списком идентификаторов, а затем сопоставляет каждый из этих идентификаторов с транспортным средством. Когда вы используете только одну карту с двойным ключом, для доступа и сохранения значений требуется всего один поиск вместо двух.
Недостатком этого способа является то, что вы не можете создать производителя, у которого нет автомобилей. Также становится сложнее (не невозможно, а просто сложнее) составить список всех производителей или всех автомобилей одного производителя.
Большая разница между Rust и PHP заключается в контроле данных и владении ими. В Rust вы должны доказать компилятору, что все, что вы делаете, действительно. В PHP предполагается, что вы знаете, что делаете.
Вот функциональный пример того, что может работать. Однако этот код не был тщательно протестирован и плохо обрабатывает ошибки. В частности, Bids::get_entry
будет panic
, если вы попытаетесь получить доступ к несуществующему автомобилю. Bids::handle_bid
и Bids::print_current_bid
также сделают это. Развертки могут быть удалены и обработаны разумно. Например, Bids::entry
лучше вернуть Option<&BidInfo>
. Причина, по которой я этого не сделал, заключается в том, что пример, который я показал, уже был довольно длинным, и я хотел, чтобы код был как можно меньше.
use std::collections::HashMap;
struct BidInfo {
/// The current bid
current_bid: f64,
/// The time of the last bid, as a unix epoch
last_bid_time: i64,
}
impl BidInfo {
/// Create a new vehicle
fn new() -> BidInfo {
BidInfo {
current_bid: 0.0,
last_bid_time: 0,
}
}
}
// To use CarInfo as the key of a HashMap, Rust needs to know how to:
// * hash it
// * test if it's equal to something else
//
// Using derive, we tell Rust to automatically generate the code to do this.
#[derive(PartialEq, Eq, Hash, Clone)]
struct CarInfo {
manufacturer: String,
model_id: u8,
}
impl CarInfo {
fn new(manufacturer: &str, model_id: u8) -> CarInfo {
CarInfo {
manufacturer: manufacturer.to_string(),
model_id: model_id,
}
}
}
// This type is the key
// vvvvvvv
struct Bids (HashMap<CarInfo, BidInfo>);
// This type is the value ^^^^^^^
impl Bids {
/// Create a new, empty, bids object
fn new() -> Bids {
Bids(HashMap::new())
}
/// Add a new car to the table
/// Assume that it's current bid info is empty
fn add_entry(&mut self, car: CarInfo) {
self.0.insert(car, BidInfo::new());
}
/// Get the current bid info for a given car
/// Note, we cannot modify this entry, we can just read it
/// If the car does not exist, this function throws an error
fn get_entry(&self, car: &CarInfo) -> &BidInfo {
self.0.get(car).unwrap()
}
/// Handle a new bid
fn handle_bid(&mut self, car: &CarInfo, bid: f64) {
// We use get_mut because we want to edit what we get back
// This gives us an Option<BidInfo>. If the car was in the map, it will
// contain the bid info, but if the car does not exist, it will be a
// special value, None
//
// This unwrap function assumes that it worked. If we try to unwrap a
// value that is None, we will get a runtime error
let mut entry = self.0.get_mut(car).unwrap();
entry.current_bid = bid;
entry.last_bid_time = entry.last_bid_time + 1; // This doesn't actually set timestamps, but you get the idea
}
/// Helper function that gives us information about the current bid of a car
fn print_current_bid(&self, car: &CarInfo) {
println!(
"Current bid of {} model {} is {}",
car.manufacturer,
car.model_id,
self.get_entry(car).current_bid
);
}
}
fn main() {
// For this example, we'll use two different manufacturers, each with 3
// cars
// Create empty map
let mut bids = Bids::new();
// Create a new car with manufacturer Volvo and model_id 0
bids.add_entry(CarInfo::new("Volvo", 0));
bids.add_entry(CarInfo::new("Volvo", 1));
bids.add_entry(CarInfo::new("Volvo", 32));
// We can also create entries this way
let bmw_car_0 = CarInfo::new("BMW", 0);
let bmw_car_1 = CarInfo::new("BMW", 1);
let bmw_car_3 = CarInfo::new("BMW", 3);
// We use clone here to create a copy of the car data, so we can keep the one we created
bids.add_entry(bmw_car_0.clone());
bids.add_entry(bmw_car_1.clone());
bids.add_entry(bmw_car_3.clone());
// Print out some state to verify everything is okay
bids.print_current_bid(&CarInfo::new("Volvo", 1));
// This method takes a &CarInfo instead of CarInfo. This means that instead
// of taking the object, it just borrows it for a bit. Therefore, we don't
// have to use clone this time
bids.print_current_bid(&bmw_car_1);
// This would cause an error, because the entry doesn't exist
//bids.print_current_bid(CarInfo::new("BMW", 33));
// Handle a few bids
bids.handle_bid(&CarInfo::new("Volvo", 1), 100.0);
bids.handle_bid(&bmw_car_1, 322.0);
// Print out the same info
bids.print_current_bid(&CarInfo::new("Volvo", 1));
bids.print_current_bid(&bmw_car_1);
}
@Shepmaster, честно говоря, я в основном имел в виду свободное использование unwrap и общее отсутствие кода, обрабатывающего несуществующие записи. Будет отредактирован вопрос, чтобы предоставить больше информации
Я имею в виду, что вы можете специально указать, что им не следует копировать: «не делайте xxx()
, потому что ууу». Тогда, по крайней мере, это не сбивает с толку.
Большое спасибо, @Carson. Особенно ко всем комментариям. Столько АГА! и несколько? но почему?.
user3019799 заходите в чат! Буду рад попытаться помочь ответить на любые вопросы. @Shepmaster не уверен в прецеденте, должен ли я создать комнату для нас двоих или просто скинуть ссылку на комнату ржавчины?
думаю и то и другое нормально
@user3019799 Присоединяйтесь к этому чату, я постараюсь ответить на любые ваши вопросы: chat.stackoverflow.com/rooms/243666/rust-hashmap-help-session
Показанный вами код Rust синтаксически недействителен. Вы можете перечитать глава о структурах в Язык программирования Rust.